diff --git a/.bazelignore b/.bazelignore index e29a94b637..a4f2c566f4 100644 --- a/.bazelignore +++ b/.bazelignore @@ -1,5 +1,7 @@ node_modules dist +aio/content aio/node_modules aio/tools/examples/shared/node_modules integration/bazel +packages/bazel/node_modules diff --git a/.bazelrc b/.bazelrc index 59fd7b8332..a778c2bf5e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,5 +1,3 @@ -# Load any settings specific to the current user -try-import .bazelrc.user ################################ # Settings for Angular team members only ################################ @@ -12,8 +10,10 @@ build:angular-team --remote_http_cache=https://storage.googleapis.com/angular-te # Make compilation fast, by keeping a few copies of the compilers # running as daemons, and cache SourceFile AST's to reduce parse time. -build --strategy=TypeScriptCompile=worker build --strategy=AngularTemplateCompile=worker +# TODO(alexeagle): re-enable after fixing worker instability with rxjs typings +# build --strategy=TypeScriptCompile=worker +build --strategy=TypeScriptCompile=standalone # Enable debugging tests with --config=debug test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results @@ -35,8 +35,9 @@ test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test # See https://github.com/bazelbuild/bazel/issues/4603 build --symlink_prefix=dist/ -# Performance: avoid stat'ing input files -build --watchfs +# Disable watchfs as it causes tests to be flaky on Windows +# https://github.com/angular/angular/issues/29541 +build --nowatchfs # Turn off legacy external runfiles run --nolegacy_external_runfiles @@ -52,6 +53,22 @@ build --incompatible_strict_action_env run --incompatible_strict_action_env test --incompatible_strict_action_env +############################### +# Saucelabs support # +# Turn on these settings with # +# --config=saucelabs # +############################### + +# Expose SauceLabs environment to actions +# These environment variables are needed by +# web_test_karma to run on Saucelabs +test:saucelabs --action_env=SAUCE_USERNAME +test:saucelabs --action_env=SAUCE_ACCESS_KEY +test:saucelabs --action_env=SAUCE_READY_FILE +test:saucelabs --action_env=SAUCE_PID_FILE +test:saucelabs --action_env=SAUCE_TUNNEL_IDENTIFIER +test:saucelabs --define=KARMA_WEB_TEST_MODE=SL_REQUIRED + ############################### # Release support # # Turn on these settings with # @@ -99,16 +116,21 @@ build --define=compile=legacy # --config=remote ############################### -# Load default settings for Remote Build Execution -# When updating, the URLs of bazel_toolchains in packages/bazel/package.bzl -# may also need to be updated (see https://github.com/angular/angular/pull/27935) -import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/bazel-0.21.0.bazelrc +# Load default settings for Remote Build Execution. +import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/.bazelrc.notoolchain # Increase the default number of jobs by 50% because our build has lots of # parallelism build:remote --jobs=150 -# Point to our custom execution platform; see tools/BUILD.bazel +# Toolchain and platform related flags +build:remote --host_javabase=@rbe_ubuntu1604_angular//java:jdk +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 build:remote --platforms=//tools:rbe_ubuntu1604-angular @@ -119,3 +141,7 @@ build:remote --remote_instance_name=projects/internal-200822/instances/default_i # Do not accept remote cache. # We need to understand the security risks of using prior build artifacts. build:remote --remote_accept_cached=false + +# Load any settings specific to the current user. Needs to be last statement in this +# config, as the user configuration should be able to overwrite flags from this file. +try-import .bazelrc.user diff --git a/.circleci/config.yml b/.circleci/config.yml index 389949576d..682bc0c2e6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,13 +26,28 @@ var_2: &browsers_docker_image circleci/node:10.12-browsers # **NOTE 1 **: If you change the cache key prefix, also sync the restore_cache 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 v2-angular-node-10.12-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }} +var_3: &cache_key v3-angular-node-10.12-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} -# Define common ENV vars -var_4: &define_env_vars +# Initializes the CI environment by setting up common environment variables. +var_4: &init_environment run: - name: Define environment variables - command: ./.circleci/env.sh + name: Initializing environment (setting up variables, overwriting Yarn) + # Overwrite the yarn installed in the docker container with our own version. + command: | + ./.circleci/env.sh + ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.13.0/bin/yarn.js) + sudo chmod a+x $ourYarn + sudo ln -fs $ourYarn /usr/local/bin/yarn + echo "Yarn version: $(yarn --version)" + + # Add GitHub to known hosts. + mkdir -p ~/.ssh + echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts + + # use git+ssh instead of https + git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true + git config --global gc.auto 0 || true + var_5: &setup_bazel_remote_execution run: @@ -48,11 +63,20 @@ var_6: &job_defaults docker: - image: *default_docker_image -# After checkout, rebase on top of master. -# Similar to travis behavior, but not quite the same. -# See https://discuss.circleci.com/t/1662 +# After checkout, rebase on top of target branch. var_7: &post_checkout - post: git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge" + run: + name: Rebase PR on target branch + command: > + if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then + # User is required for rebase. + 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} + else + echo "This build is not over a PR, nothing to do." + fi var_8: &yarn_install run: @@ -70,33 +94,63 @@ var_9: &setup_circleci_bazel_config name: Setting up CircleCI bazel configuration command: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc -# Sets up Yarn by downloading it and installing it globally. We don't use Yarn from the -# docker image because this means that we can only use the Yarn version that comes with -# a specific version of NodeJS. We want to be able to update Yarn without having to -# update the docker image that comes with NodeJS. -var_10: &download_yarn - run: - name: Downloading Yarn - command: curl -o- -L https://yarnpkg.com/install.sh | PROFILE=$BASH_ENV bash -s -- --version "$CI_YARN_VERSION" - -var_11: &restore_cache +var_10: &restore_cache restore_cache: keys: - *cache_key # This fallback should be the cache_key without variables. - - v2-angular-node-10.12- + - v3-angular-node-10.12- + +# Branch filter that can be specified for jobs that should only run on publish branches +# (e.g. master or the patch branch) +var_12: &publish_branches_filter + branches: + only: + - master + # e.g. 7.0.x, 7.1.x, etc. + - /\d+\.\d+\.x/ + +# Workspace initially persisted by the `install` job, and then enhanced by `test_aio` and +# `build-npm-packages`. +# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs +# https://circleci.com/blog/deep-diving-into-circleci-workspaces/ +var_13: &attach_workspace + attach_workspace: + at: ~/ version: 2 jobs: + setup: + <<: *job_defaults + steps: + - checkout + - *post_checkout + # This cache is saved in the build-npm-packages so that Bazel cache is also included. + - *restore_cache + - *init_environment + - *yarn_install + - run: yarn --cwd aio install --frozen-lockfile --non-interactive + # Make the bazel directories and add a file to them if they don't exist already so that + # persist_to_workspace does not fail. + - run: | + if [ ! -d ~/bazel_repository_cache ]; then + mkdir ~/bazel_repository_cache + touch ~/bazel_repository_cache/MARKER + fi + # Persist any changes at this point to be reused by further jobs. + # **NOTE 1 **: Folders persisted here should be kept in sync with `var_13: &attach_workspace`. + # **NOTE 2 **: To add new content to the workspace, always persist on the same root. + - persist_to_workspace: + root: ~/ + paths: + - ./ng + - ./bazel_repository_cache + lint: <<: *job_defaults steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install + - *attach_workspace + - *init_environment - run: 'yarn bazel:format -mode=check || (echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)' @@ -104,39 +158,31 @@ jobs: - run: 'yarn bazel:lint || (echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)' - - run: ./node_modules/.bin/gulp lint + - run: yarn gulp lint test: <<: *job_defaults resource_class: xlarge steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install + - *attach_workspace + - *init_environment - *setup_circleci_bazel_config - + # Enable remote/sibling docker which is needed by auto-selection of toolchain configs for RBE. + - setup_remote_docker # Setup remote execution and run RBE-compatible tests. - *setup_bazel_remote_execution - - run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-local - # Now run RBE incompatible tests locally. - - run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc - - run: yarn bazel test //... --build_tag_filters=-ivy-only,local --test_tag_filters=-ivy-only,local + - run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only # Temporary job to test what will happen when we flip the Ivy flag to true test_ivy_aot: <<: *job_defaults resource_class: xlarge steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install + - *attach_workspace + - *init_environment - *setup_circleci_bazel_config + # Enable remote/sibling docker which is needed by auto-selection of toolchain configs for RBE. + - setup_remote_docker - *setup_bazel_remote_execution # We need to explicitly specify the --symlink_prefix option because otherwise we would @@ -163,17 +209,43 @@ jobs: path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br destination: core/todo/bundle.br + test_saucelabs_bazel: + <<: *job_defaults + # In order to avoid the bottleneck of having a slow host machine, we acquire a better + # container for this job. This is necessary because we launch a lot of browsers concurrently + # and therefore the tunnel and Karma need to process a lot of file requests and tests. + resource_class: xlarge + steps: + - *attach_workspace + - *init_environment + - *setup_circleci_bazel_config + - run: + name: Preparing environment for running tests on Saucelabs. + command: setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev) + - run: + name: Starting Saucelabs tunnel + command: ./scripts/saucelabs/start-tunnel.sh + background: true + # Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests + # too early without Saucelabs not being ready. + - run: ./scripts/saucelabs/wait-for-tunnel.sh + # All web tests are contained within a single //:test_web_all target for Saucelabs + # as running each set of tests as a separate target will attempt to acquire too + # many browsers on Saucelabs (7 per target currently) and some tests will always + # fail to acquire browsers. For example: + # 14 02 2019 19:52:33.170:WARN [launcher]: chrome beta on SauceLabs have not captured in 180000 ms, killing. + # //packages/forms/test:web_test_sauce TIMEOUT in 315.0s + - run: yarn bazel test --config=saucelabs //:test_web_all + - run: ./scripts/saucelabs/stop-tunnel.sh + test_aio: <<: *job_defaults docker: # Needed because the AIO tests and the PWA score test depend on Chrome being available. - image: *browsers_docker_image steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment # Build aio - run: yarn --cwd aio build --progress=false # Lint the code @@ -197,13 +269,10 @@ jobs: # Needed because before deploying the deploy-production script runs the PWA score tests. - image: *browsers_docker_image steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment # Deploy angular.io to production (if necessary) - - run: setPublicVar CI_STABLE_BRANCH "$(npm info @angular/core dist-tags.latest | sed -r 's/^\s*([0-9]+\.[0-9]+)\.[0-9]+.*$/\1.x/')" + - run: setPublicVar_CI_STABLE_BRANCH - run: yarn --cwd aio deploy-production test_aio_local: @@ -212,13 +281,8 @@ jobs: # Needed because the AIO tests and the PWA score test depend on Chrome being available. - image: *browsers_docker_image steps: - - checkout: - <<: *post_checkout - - *restore_cache - - attach_workspace: - at: dist - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment # Build aio (with local Angular packages) - run: yarn --cwd aio build-local --progress=false # Run PWA-score tests @@ -231,27 +295,27 @@ jobs: test_aio_local_ivy: <<: *job_defaults + docker: + # Needed because the AIO tests and the PWA score test depend on Chrome being available. + - image: *browsers_docker_image steps: - - checkout: - <<: *post_checkout - - *restore_cache - - attach_workspace: - at: dist - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment # Build aio with Ivy (using local Angular packages) - run: yarn --cwd aio build-with-ivy --progress=false + # Run PWA-score tests + # (Run before unit and e2e tests, which destroy the `dist/` directory.) + - run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE + # Run unit tests + - run: yarn --cwd aio test --progress=false --watch=false + # Run e2e tests + - run: yarn --cwd aio e2e --configuration=ci test_aio_tools: <<: *job_defaults steps: - - checkout: - <<: *post_checkout - - *restore_cache - - attach_workspace: - at: dist - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment # Install - run: yarn --cwd aio install --frozen-lockfile --non-interactive - run: yarn --cwd aio extract-cli-command-docs @@ -264,42 +328,43 @@ jobs: docker: # Needed because the example e2e tests depend on Chrome. - image: *browsers_docker_image - parallelism: 3 + parallelism: 4 + resource_class: xlarge steps: - - checkout: - <<: *post_checkout - - *restore_cache - - attach_workspace: - at: dist - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment # Install aio - run: yarn --cwd aio install --frozen-lockfile --non-interactive # Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled. # Since the parallelism is set to "3", there will be three parallel CircleCI containers # with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument. - - run: yarn --cwd aio example-e2e --setup --local --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} + - run: yarn --cwd aio example-e2e --setup --local --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} test_docs_examples_ivy: <<: *job_defaults docker: # Needed because the example e2e tests depend on Chrome. - image: *browsers_docker_image - parallelism: 3 + resource_class: xlarge + # We increase the parallelism here to five while the "test_docs_examples" job runs with + # a parallelism of four. This is necessary because this job also need to run NGCC which + # takes up more time and we don't want these jobs to impact the overall CI turnaround. + parallelism: 5 steps: - - checkout: - <<: *post_checkout - - *restore_cache - - attach_workspace: - at: dist - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment # Install aio - run: yarn --cwd aio install --frozen-lockfile --non-interactive + # 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 with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled. # Since the parallelism is set to "3", there will be three parallel CircleCI containers # with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument. - - run: yarn --cwd aio example-e2e --setup --local --ivy --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} + - run: yarn --cwd aio example-e2e --setup --local --ivy --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} # This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`. aio_preview: @@ -307,11 +372,8 @@ jobs: environment: AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz' steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment - run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH $CI_PULL_REQUEST $CI_COMMIT - store_artifacts: path: *aio_preview_artifact_path @@ -327,11 +389,8 @@ jobs: # Needed because the test-preview script runs e2e tests and the PWA score test with Chrome. - image: *browsers_docker_image steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment - run: yarn --cwd aio install --frozen-lockfile --non-interactive - run: name: Wait for preview and run tests @@ -350,53 +409,48 @@ jobs: <<: *job_defaults resource_class: xlarge steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install + - *attach_workspace + - *init_environment - *setup_circleci_bazel_config + # Enable remote/sibling docker which is needed by auto-selection of toolchain configs for RBE. + - setup_remote_docker - *setup_bazel_remote_execution - run: scripts/build-packages-dist.sh # Save the npm packages from //packages/... for other workflow jobs to read - # https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs - persist_to_workspace: - root: dist + root: ~/ paths: - - packages-dist + - ng/dist/packages-dist + # Save dependencies and bazel repository cache to use on subsequent runs. - save_cache: key: *cache_key paths: - "node_modules" + - "aio/node_modules" - "~/bazel_repository_cache" - # Build the ivy npm packages. build-ivy-npm-packages: <<: *job_defaults resource_class: xlarge steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install + - *attach_workspace + - *init_environment - *setup_circleci_bazel_config + # Enable remote/sibling docker which is needed by auto-selection of toolchain configs for RBE. + - setup_remote_docker - *setup_bazel_remote_execution - run: scripts/build-ivy-npm-packages.sh # Save the npm packages from //packages/... for other workflow jobs to read - # https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs - persist_to_workspace: - root: dist + root: ~/ paths: - - packages-dist-ivy-aot + - 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. @@ -415,15 +469,8 @@ jobs: # on a 4G worker so we use a larger machine here too. resource_class: xlarge steps: - - checkout: - <<: *post_checkout - - *restore_cache - - attach_workspace: - at: dist - - *define_env_vars - - *download_yarn - # Some integration tests get their dependencies from the root `node_modules/`. - - *yarn_install + - *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} @@ -433,21 +480,20 @@ jobs: publish_snapshot: <<: *job_defaults steps: - - checkout: - <<: *post_checkout - - *define_env_vars # See below - ideally this job should not trigger for non-upstream builds. # But since it does, we have to check this condition. - run: name: Skip this job for Pull Requests and Fork builds - # Note, `|| true` on the end makes this step always exit 0 - command: '[[ - "$CI_PULL_REQUEST" != "false" - || "$CI_REPO_OWNER" != "angular" - || "$CI_REPO_NAME" != "angular" - ]] && circleci step halt || true' - - attach_workspace: - at: dist + # Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this + # step can be run before `init_environment`. + command: > + if [[ -n "${CIRCLE_PR_NUMBER}" ]] || + [[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] || + [[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then + circleci step halt + fi + - *attach_workspace + - *init_environment # CircleCI has a config setting to force SSH for all github connections # This is not compatible with our mechanism of using a Personal Access Token # Clear the global setting @@ -457,21 +503,24 @@ jobs: command: 'openssl aes-256-cbc -d -in .circleci/github_token -k "${KEY}" -out ~/.git_credentials' - run: ./scripts/ci/publish-build-artifacts.sh - aio_monitoring: + aio_monitoring_stable: <<: *job_defaults docker: # 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. - image: *browsers_docker_image steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn + - *attach_workspace + - *init_environment + - run: setPublicVar_CI_STABLE_BRANCH - run: - name: Run tests against the deployed apps - command: ./aio/scripts/test-production.sh $CI_AIO_MIN_PWA_SCORE + name: Check out `aio/` from the stable branch + command: | + git fetch origin $CI_STABLE_BRANCH + git checkout --force origin/$CI_STABLE_BRANCH -- aio/ + - run: + name: Run tests against https://angular.io/ + command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE - run: name: Notify caretaker about failure # `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings. @@ -479,20 +528,24 @@ jobs: command: 'curl --request POST --header "Content-Type: application/json" --data "{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}" $SLACK_CARETAKER_WEBHOOK_URL' when: on_fail - legacy-unit-tests-local: + aio_monitoring_next: <<: *job_defaults docker: + # 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. - image: *browsers_docker_image steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install - - run: yarn tsc -p packages - - run: yarn tsc -p modules - - run: yarn karma start ./karma-js.conf.js --single-run --browsers=ChromeNoSandbox + - *attach_workspace + - *init_environment + - run: + name: Run tests against https://next.angular.io/ + command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE + - run: + name: Notify caretaker about failure + # `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings. + # The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci. + command: 'curl --request POST --header "Content-Type: application/json" --data "{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}" $SLACK_CARETAKER_WEBHOOK_URL' + when: on_fail legacy-unit-tests-saucelabs: <<: *job_defaults @@ -501,12 +554,8 @@ jobs: # and therefore the tunnel and Karma need to process a lot of file requests and tests. resource_class: xlarge steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install + - *attach_workspace + - *init_environment - run: name: Preparing environment for running tests on Saucelabs. command: | @@ -527,14 +576,8 @@ jobs: legacy-misc-tests: <<: *job_defaults steps: - - checkout: - <<: *post_checkout - - *restore_cache - - *define_env_vars - - *download_yarn - - *yarn_install - - attach_workspace: - at: dist + - *attach_workspace + - *init_environment - run: yarn gulp check-cycle # TODO: disabled because the Bazel packages-dist does not seem to have map files for # the ESM5/ES2015 output. See: https://github.com/angular/angular/issues/27966 @@ -547,27 +590,70 @@ jobs: resource_class: xlarge docker: - image: *browsers_docker_image + # The Material unit tests support splitting the browsers across multiple CircleCI + # instances. Since by default this job launches two browsers, we run each browser + # in its own container instance. + # https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L107-L110 + parallelism: 2 + environment: + # The Material unit tests also support launching the same browser multiple times by + # sharding individual specs across the defined multiple instances. + # See: https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L113-L116 + KARMA_PARALLEL_BROWSERS: 3 steps: - - checkout: - <<: *post_checkout - - *define_env_vars - - *download_yarn - - attach_workspace: - at: dist - - run: ./scripts/ci/run_angular_material_unit_tests.sh + - *attach_workspace + - *init_environment + - run: + name: "Cloning Material repository" + command: ./scripts/ci/clone_angular_material_repo.sh + - restore_cache: + # Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable. + # It needs to be hardcoded here, because env variables interpolation is not supported. + keys: + - v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }} + - v2-angular-material- + - run: + name: Installing Material dependencies. + command: yarn --cwd ${MATERIAL_REPO_TMP_DIR} install --frozen-lockfile --non-interactive + # Save the cache before we run the Material unit tests script. This is necessary + # because we don't want to cache the node modules which have been modified to contain + # the attached Ivy package output. + - save_cache: + # Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable. + # It needs to be hardcoded here, because env variables interpolation is not supported. + key: v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }} + paths: + - "/tmp/material2/node_modules" + - run: + name: "Running Material unit tests" + command: ./scripts/ci/run_angular_material_unit_tests.sh workflows: version: 2 default_workflow: jobs: - - lint - - test - - test_ivy_aot - - build-npm-packages - - build-ivy-npm-packages - - test_aio - - legacy-unit-tests-local - - legacy-unit-tests-saucelabs + - setup + - lint: + requires: + - setup + - test: + requires: + - setup + - test_ivy_aot: + requires: + - setup + - build-npm-packages: + requires: + - setup + - build-ivy-npm-packages: + requires: + - setup + - test_aio: + requires: + - setup + - legacy-unit-tests-saucelabs: + requires: + - setup - deploy_aio: requires: - test_aio @@ -588,8 +674,10 @@ workflows: - build-npm-packages - test_docs_examples_ivy: requires: - - build-npm-packages + - build-ivy-npm-packages - aio_preview: + requires: + - setup # Only run on PR builds. (There can be no previews for non-PR builds.) filters: branches: @@ -619,20 +707,41 @@ workflows: # since the publishing script expects the legacy outputs layout. - build-npm-packages - build-ivy-npm-packages - - legacy-misc-tests - - legacy-unit-tests-local - legacy-unit-tests-saucelabs + - legacy-misc-tests - material-unit-tests: requires: - build-ivy-npm-packages + saucelabs_tests: + jobs: + - setup + - test_saucelabs_bazel: + requires: + - setup + triggers: + - schedule: + # Runs the Saucelabs legacy tests every hour. We still want to run Saucelabs + # frequently as the caretaker needs up-to-date results when merging PRs or creating + # a new release. Also we primarily moved the Saucelabs job into a cronjob that doesn't + # run for PRs, in order to ensure that PRs are not affected by Saucelabs flakiness or + # incidents. This is still guaranteed (even if we run the job every hour). + cron: "0 * * * *" + filters: *publish_branches_filter aio_monitoring: jobs: - - aio_monitoring + - setup + - aio_monitoring_stable: + requires: + - setup + - aio_monitoring_next: + requires: + - setup triggers: - schedule: - cron: "0 0 * * *" + # Runs AIO monitoring jobs at 10:00AM every day. + cron: "0 10 * * *" filters: branches: only: diff --git a/.circleci/env-helpers.inc.sh b/.circleci/env-helpers.inc.sh index ff21eb9ea0..5fa1263e11 100644 --- a/.circleci/env-helpers.inc.sh +++ b/.circleci/env-helpers.inc.sh @@ -36,3 +36,38 @@ function setSecretVar() { # Restore original shell options. eval "$originalShellOptions"; } + + +# Create a function to set an environment variable, when called. +# +# Use this function for creating setter for public environment variables that require expensive or +# time-consuming computaions and may not be needed. When needed, you can call this function to set +# the environment variable (which will be available through `$BASH_ENV` from that point onwards). +# +# Arguments: +# - ``: The name of the environment variable. The generated setter function will be +# `setPublicVar_`. +# - ``: The code to run to compute the value for the variable. Since this code should be +# executed lazily, it must be properly escaped. For example: +# ```sh +# # DO NOT do this: +# createPublicVarSetter MY_VAR "$(whoami)"; # `whoami` will be evaluated eagerly +# +# # DO this isntead: +# createPublicVarSetter MY_VAR "\$(whoami)"; # `whoami` will NOT be evaluated eagerly +# ``` +# +# Usage: `createPublicVarSetter ` +# +# Example: +# ```sh +# createPublicVarSetter MY_VAR 'echo "FOO"'; +# echo $MY_VAR; # Not defined +# +# setPublicVar_MY_VAR; +# source $BASH_ENV; +# echo $MY_VAR; # FOO +# ``` +function createPublicVarSetter() { + echo "setPublicVar_$1() { setPublicVar $1 \"$2\"; }" >> $BASH_ENV; +} diff --git a/.circleci/env.sh b/.circleci/env.sh index 2df630329b..4ebc6cdc8e 100755 --- a/.circleci/env.sh +++ b/.circleci/env.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash # Variables -readonly envHelpersPath="`dirname $0`/env-helpers.inc.sh"; -readonly getCommitRangePath="`dirname $0`/get-commit-range.js"; +readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") +readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; +readonly getCommitRangePath="$projectDir/.circleci/get-commit-range.js"; # Load helpers and make them available everywhere (through `$BASH_ENV`). source $envHelpersPath; @@ -14,7 +15,7 @@ echo "source $envHelpersPath;" >> $BASH_ENV; #################################################################################################### # See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. #################################################################################################### -setPublicVar PROJECT_ROOT "$(pwd)"; +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"; @@ -31,7 +32,13 @@ setPublicVar CI_COMMIT_RANGE "`[[ ${CIRCLE_PR_NUMBER:-false} != false ]] && echo setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}"; setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME"; setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME"; -setPublicVar CI_YARN_VERSION "1.13.0"; + + +#################################################################################################### +# Define "lazy" PUBLIC environment variables for CircleCI. +# (I.e. functions to set an environment variable when called.) +#################################################################################################### +createPublicVarSetter CI_STABLE_BRANCH "\$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')"; #################################################################################################### @@ -54,6 +61,7 @@ else setPublicVar SAUCE_USERNAME "angular-ci"; setSecretVar SAUCE_ACCESS_KEY "9b988f434ff8-fbca-8aa4-4ae3-35442987"; fi +setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}" @@ -61,6 +69,15 @@ setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_ # acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout. setPublicVar SAUCE_READY_FILE_TIMEOUT 120 +#################################################################################################### +# Define environment variables for the Angular Material unit tests job. +#################################################################################################### +# We specifically use a directory within "/tmp" here because we want the cloned repo to be +# completely isolated from angular/angular in order to avoid any bad interactions between +# their separate build setups. +setPublicVar MATERIAL_REPO_TMP_DIR "/tmp/material2" +setPublicVar MATERIAL_REPO_URL "https://github.com/angular/material2.git" +setPublicVar MATERIAL_REPO_BRANCH "ivy-2019" # Source `$BASH_ENV` to make the variables available immediately. source $BASH_ENV; diff --git a/.circleci/get-commit-range.js b/.circleci/get-commit-range.js index f8cd211213..75477105e7 100644 --- a/.circleci/get-commit-range.js +++ b/.circleci/get-commit-range.js @@ -141,7 +141,7 @@ function getJson(url) { const opts = {headers: {Accept: 'application/json'}}; const onResponse = res => { const statusCode = res.statusCode || -1; - const isSuccess = (200 <= statusCode) && (statusCode <= 400); + const isSuccess = (200 <= statusCode) && (statusCode < 400); let responseText = ''; res. diff --git a/.codefresh/Dockerfile.win-1809 b/.codefresh/Dockerfile.win-1809 new file mode 100644 index 0000000000..c5169f97eb --- /dev/null +++ b/.codefresh/Dockerfile.win-1809 @@ -0,0 +1,103 @@ +ARG core=mcr.microsoft.com/windows/servercore:1809 +ARG target=mcr.microsoft.com/powershell:windowsservercore-1809 + +FROM $core as download + +ARG node_version=10.13.0 +ARG yarn_version=1.13.0 + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +ENV GPG_VERSION 2.3.4 + +RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; \ + Start-Process .\gpg4win.exe -ArgumentList '/S' -NoNewWindow -Wait + +RUN @( \ + '94AE36675C464D64BAFA68DD7434390BDBE9B9C5', \ + 'FD3A5288F042B6850C66B31F09FE44734EB7990E', \ + '71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', \ + 'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', \ + 'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', \ + 'B9AE9905FFD7803F25714661B63B535A4C206CA9', \ + '77984A986EBC2AA786BC0F66B01FBB92821C587A', \ + '8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', \ + '4ED778F539E3634C779C87C6D7062848A1AB005C', \ + 'A48C2BEE680E841632CD4E44F07496B3EB3C1762', \ + 'B9E2F5981AA6E0CD28160D9FF13993A75599653C' \ + ) | foreach { \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; \ + } + +ENV NODE_VERSION=$node_version + +RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; \ + gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc + +RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; \ + $sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; \ + if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; \ + Expand-Archive node.zip -DestinationPath C:\ ; \ + Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs' + +ENV YARN_VERSION=$yarn_version + +RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \ + Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; \ + $sig = Get-AuthenticodeSignature yarn.msi ; \ + if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; \ + Write-Output $sig.SignerCertificate.Thumbprint ; \ + if (@( \ + '7E253367F8A102A91D04829E37F3410F14B68A5F', \ + 'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' \ + ) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; \ + Start-Process msiexec.exe -ArgumentList '/i', 'yarn.msi', '/quiet', '/norestart' -NoNewWindow -Wait + +ENV GIT_VERSION 2.20.1 +ENV GIT_DOWNLOAD_URL https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-busybox-64-bit.zip +ENV GIT_SHA256 9817ab455d9cbd0b09d8664b4afbe4bbf78d18b556b3541d09238501a749486c + +RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \ + Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; \ + if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; \ + Expand-Archive git.zip -DestinationPath C:\git; \ + Remove-Item git.zip + +FROM $target as baseimage + +ENV NPM_CONFIG_LOGLEVEL info + +COPY --from=download /nodejs /nodejs +COPY --from=download [ "/Program Files (x86)/yarn", "/yarn" ] +COPY --from=download /git /git + +ARG SETX=/M +RUN setx %SETX% PATH "%PATH%;C:\nodejs;C:\yarn\bin;C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin" + +CMD [ "node.exe" ] + +FROM baseimage + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; \ + Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; \ + Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; \ + Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; \ + Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; \ + Remove-Item msys2.tar.xz; \ + Remove-Item msys2.tar; \ + Remove-Item 7z.exe; \ + Remove-Item -Recurse 7zip; \ + [Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); \ + [Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine); \ + Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe; \ + Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait; \ + Remove-Item vc_redist.x64.exe + +# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container. +SHELL ["cmd", "/c"] +COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd" +ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c + +CMD ["cmd.exe"] \ No newline at end of file diff --git a/.codefresh/README.md b/.codefresh/README.md new file mode 100644 index 0000000000..0f6ec57900 --- /dev/null +++ b/.codefresh/README.md @@ -0,0 +1,33 @@ +# CodeFresh configuration + +[![Codefresh build status](https://g.codefresh.io/api/badges/pipeline/angular/angular%2Fangular%2Fangular?type=cf-1)](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular) + +This folder contains configuration for the [CodeFresh]() based CI checks for this repository. + +## The build pipeline + +CodeFresh uses a several pipeline for each repository. The `codefresh.yml` file defines pipeline [build steps](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/) for this repository. + +Run results can be seen in the GitHub checks interface and in the [public pipeline](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular) + +Although most configuration is done via `pipeline.yml`, some options are only available in the online [pipeline settings](https://g.codefresh.io/pipelines/angular/services?repoOwner=angular&repoName=angular&project=angular%2Fangular&context=github&serviceName=angular%2Fangular), which needs a login to access. + + +## Caretaker + +CodeFresh status can be found at . + +Issues related to the CodeFresh setup should be escalated to the Tools Team via the current caretaker, followed by Alex Eagle and Filipe Silva. + +## Rollout strategy + +Currently it is only used for tests on Windows platforms, on the master branch, and without pushing user-facing reports. It's only possible to see current builds in the [public pipeline dashboard](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular). + +After a week or two of running like this, we should reassess how stable and reliable it is. + +Next steps include: +- building PRs +- showing build status publicly +- blocking PRs that break the build +- expanding the test suite + diff --git a/.codefresh/bazel.rc b/.codefresh/bazel.rc new file mode 100644 index 0000000000..5a2fa5ca6d --- /dev/null +++ b/.codefresh/bazel.rc @@ -0,0 +1,42 @@ +# These options are enabled when running on CI +# We do this by copying this file to /etc/bazel.bazelrc at the start of the build. +# See documentation in /docs/BAZEL.md + +# Save downloaded repositories in a location that can be cached by CodeFresh and shared between +# builds. This helps speed up the analysis time significantly with Bazel managed node dependencies +# on the CI. +# https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/#caching-the-artifacts-of-your-build-system +build --repository_cache=C:/codefresh/volume/bazel_repository_cache + +# Don't be spammy in the logs +# TODO(gmagolan): Hide progress again once build performance improves +# Presently, CircleCI can timeout during bazel test ... with the following +# error: Too long with no output (exceeded 10m0s) +build --noshow_progress + +# Print all the options that apply to the build. +# This helps us diagnose which options override others +# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc) +build --announce_rc + +# Workaround https://github.com/bazelbuild/bazel/issues/3645 +# Bazel doesn't calculate the memory ceiling correctly when running under Docker. +# Limit Bazel to consuming resources that fit in CodeFresh VMs +# TODO(filipesilva): determine the correct memory limit +build --local_resources=8000,8.0,1.0 + +# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309 +test --flaky_test_attempts=2 + +# More details on failures +build --verbose_failures=true + +# Include PATH in Windows build/tests +# https://github.com/bazelbuild/rules_typescript/pull/356 +build --action_env=PATH +test --action_env=PATH --test_env=PATH + +# Exclude tests known to not work on Windows. + +# Chrome web tests are currently broken. +test --test_tag_filters=-browser:chromium-local diff --git a/.codefresh/codefresh.yml b/.codefresh/codefresh.yml new file mode 100644 index 0000000000..7316835c66 --- /dev/null +++ b/.codefresh/codefresh.yml @@ -0,0 +1,26 @@ +version: '1.0' + +steps: + BuildImage: + type: build + image_name: node-bazel-windows + working_directory: ./.codefresh + no_cf_cache: true + build_arguments: + - node_version=10.13.0 + - yarn_version=1.13.0 + dockerfile: ./Dockerfile.win-1809 + + RunTests: + title: Run Example + image: ${{BuildImage}} + commands: + # Install dependencies + - yarn install --frozen-lockfile --non-interactive --network-timeout 100000 --no-progress + # Add Bazel CI config + - copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc + # Run tests + - yarn bazel test //tools/ts-api-guardian:all //packages/language-service/test + - yarn test-ivy-aot //packages/animations/test //packages/common/test //packages/forms/test //packages/http/test //packages/platform-browser/test //packages/platform-browser-dynamic/test //packages/router/test + - yarn bazel test //tools/public_api_guard/... + - yarn bazel test //packages/compiler-cli/integrationtest:integrationtest //packages/compiler-cli/test/compliance:compliance diff --git a/.codefresh/fix-msys64.cmd b/.codefresh/fix-msys64.cmd new file mode 100644 index 0000000000..3869596d08 --- /dev/null +++ b/.codefresh/fix-msys64.cmd @@ -0,0 +1,6 @@ +@echo off +REM Fix for https://github.com/docker/for-win/issues/2920 +REM echo "Fixing msys64 folder..." +REM Touch all .dll files inside C:\msys64\ +forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL" +REM echo "Fixed msys64 folder." \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 0acf0cd70d..b45635737c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,5 +5,8 @@ *.js eol=lf *.ts eol=lf +# API guardian patch must always use LF for tests to work +*.patch eol=lf + # Must keep Windows line ending to be parsed correctly scripts/windows/packages.txt eol=crlf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6e593be73a..5c7fe7b236 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -39,12 +39,14 @@ # (just to make this file easier to understand) # ================================================ +# alan-agius4 - Alan Agius # alexeagle - Alex Eagle # alxhub - Alex Rickabaugh # AndrewKushnir - Andrew Kushnir # andrewseguin - Andrew Seguin # benlesh - Ben Lesh # brandonroberts - Brandon Roberts +# devversion - Paul Gschwendtner # filipesilva - Filipe Silva # gkalpak - George Kalpakas # hansl - Hans Larsen @@ -86,6 +88,7 @@ # - IgorMinar # - kara # - mhevery +# - alexeagle # =========================================================== @@ -215,6 +218,7 @@ # # - gkalpak # - petebacondarwin +# - jasonaden # =========================================================== @@ -272,6 +276,7 @@ # @angular/fw-docs-intro # =========================================================== # +# - jenniferfell # - brandonroberts # - IgorMinar # - stephenfluin @@ -293,6 +298,29 @@ # - IgorMinar +# =========================================================== +# @angular/tools-docs-libraries +# =========================================================== +# +# - alan-agius4 +# - alexeagle +# - hansl +# - IgorMinar +# - mgechev + + +# =========================================================== +# @angular/tools-docs-schematics +# =========================================================== +# +# - alan-agius4 +# - alexeagle +# - hansl +# - IgorMinar +# - mgechev + + + # =========================================================== # @angular/fw-docs-marketing # =========================================================== @@ -313,6 +341,9 @@ # =========================================================== # # - alexeagle +# - devversion +# - filipesilva +# - gkalpak # - IgorMinar @@ -347,10 +378,19 @@ /packages/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /packages/platform-browser/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + /aio/content/guide/animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/images/guide/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/complex-animation-sequences.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + +/aio/content/guide/reusable-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + +/aio/content/guide/route-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + +/aio/content/guide/transition-and-triggers.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + # ================================================ @@ -373,20 +413,22 @@ # ================================================ -# packages/compiler-cli/src/ngcc/ +# packages/compiler-cli/ngcc/ # ================================================ -/packages/compiler-cli/src/ngcc/** @angular/fw-ngcc @angular/framework-global-approvers +/packages/compiler-cli/ngcc/** @angular/fw-ngcc @angular/framework-global-approvers # ================================================ -# @angular/compiler-cli/ngtools +# Framework/cli integration # # a rule to control API changes between @angular/compiler-cli and @angular/cli # ================================================ /packages/compiler-cli/src/ngtools/** @angular/tools-cli @angular/framework-global-approvers +/aio/content/guide/ivy.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/web-worker.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -405,6 +447,15 @@ /packages/platform-browser-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /packages/platform-webworker/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /packages/platform-webworker-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/packages/examples/common/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + +/aio/content/guide/architecture-components.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/architecture-modules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/architecture-next-steps.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/architecture-services.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/architecture.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/architecture/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/architecture/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/attribute-directives.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/attribute-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -413,6 +464,8 @@ /aio/content/guide/bootstrapping.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/bootstrapping/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/cheatsheet.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + /aio/content/guide/component-interaction.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/images/guide/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -428,7 +481,13 @@ /aio/content/examples/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/images/guide/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/guide/dependency-injection-pattern.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/dependency-injection-navtree.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + +/aio/content/guide/dependency-injection-providers.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + +/aio/content/guide/displaying-data.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/displaying-data/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/displaying-data/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/dynamic-component-loader.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/dynamic-component-loader/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -455,12 +514,9 @@ /aio/content/images/guide/lifecycle-hooks/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/ngcontainer/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/images/guide/ngcontainer/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/examples/ngmodule/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/images/guide/ngmodule/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/ngmodule-api.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -472,6 +528,9 @@ /aio/content/guide/module-types.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/template-syntax.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/built-in-template-functions/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/event-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/interpolation/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/images/guide/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -494,6 +553,10 @@ /aio/content/examples/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/images/guide/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/user-input.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/user-input/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/user-input/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + # ================================================ @@ -519,6 +582,7 @@ /aio/content/guide/elements.md @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + # ================================================ # @angular/forms # ================================================ @@ -580,6 +644,7 @@ /packages/service-worker/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/service-worker-getting-started.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/service-worker-getting-started/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/app-shell.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/service-worker-communications.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/service-worker-config.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/service-worker-devops.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -593,6 +658,7 @@ # ================================================ /packages/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/packages/common/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /packages/examples/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/upgrade.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/upgrade-module/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -649,6 +715,7 @@ testing/** @angular/fw-test /packages/platform-browser/src/security/** @angular/fw-security /aio/content/guide/security.md @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/security/** @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/security/** @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -674,11 +741,20 @@ testing/** @angular/fw-test /aio/* @angular/docs-infra @angular/framework-global-approvers /aio/aio-builds-setup/** @angular/docs-infra @angular/framework-global-approvers +/aio/content/examples/* @angular/docs-infra @angular/framework-global-approvers /aio/scripts/** @angular/docs-infra @angular/framework-global-approvers /aio/src/** @angular/docs-infra @angular/framework-global-approvers /aio/tests/** @angular/docs-infra @angular/framework-global-approvers /aio/tools/** @angular/docs-infra @angular/framework-global-approvers +# Hidden docs +/aio/content/guide/change-log.md @angular/docs-infra @angular/framework-global-approvers +/aio/content/guide/docs-style-guide.md @angular/docs-infra @angular/framework-global-approvers +/aio/content/examples/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers +/aio/content/images/guide/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers +/aio/content/guide/visual-studio-2015.md @angular/docs-infra @angular/framework-global-approvers +/aio/content/examples/visual-studio-2015/** @angular/docs-infra @angular/framework-global-approvers + # ================================================ @@ -686,25 +762,36 @@ testing/** @angular/fw-test # ================================================ /aio/content/guide/quickstart.md @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/cli-quickstart/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/cli-quickstart/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/tutorial/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes - +/aio/content/images/guide/toh/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/toh-pt0/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/toh-pt1/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/toh-pt2/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/toh-pt3/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/toh-pt4/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/toh-pt5/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/toh-pt6/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/getting-started-v0/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes # ================================================ # Docs: observables # ================================================ -/aio/content/examples/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/images/guide/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/comparing-observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/examples/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/images/guide/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/observables-in-angular.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/examples/practical-observable-usage/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/practical-observable-usage.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/examples/rx-library/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/practical-observable-usage/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/rx-library.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/rx-library/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -715,12 +802,36 @@ testing/** @angular/fw-test /aio/content/guide/npm-packages.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/browser-support.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/typescript-configuration.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes -/aio/content/guide/setup-systemjs-anatomy.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/setup.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/setup/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/build.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/build/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/deployment.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/file-structure.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/releases.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/guide/updating.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/workspace-config.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + + + +# ================================================ +# Docs: libraries +# ================================================ + +/aio/content/guide/creating-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/using-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + + +# ================================================ +# Docs: schematics +# ================================================ + +/aio/content/guide/schematics.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/schematics-authoring.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/guide/schematics-for-libraries.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/guide/schematics/** @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/schematics-for-libraries/** @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -729,6 +840,7 @@ testing/** @angular/fw-test # ================================================ /aio/content/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/images/bios/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/images/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/navigation.json @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/license.md @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes @@ -742,18 +854,39 @@ testing/** @angular/fw-test /* @angular/fw-dev-infra /.buildkite/** @angular/fw-dev-infra /.circleci/** @angular/fw-dev-infra +/.codefresh/** @angular/fw-dev-infra /.github/** @angular/fw-dev-infra +/.vscode/** @angular/fw-dev-infra /docs/BAZEL.md @angular/fw-dev-infra +/packages/* @angular/fw-dev-infra /scripts/** @angular/fw-dev-infra /third_party/** @angular/fw-dev-infra -/tools/** @angular/fw-dev-infra +/tools/build/** @angular/fw-dev-infra +/tools/cjs-jasmine/** @angular/fw-dev-infra +/tools/gulp-tasks/** @angular/fw-dev-infra +/tools/ngcontainer/** @angular/fw-dev-infra +/tools/npm/** @angular/fw-dev-infra +/tools/npm_workspace/** @angular/fw-dev-infra +/tools/public_api_guard/** @angular/fw-dev-infra +/tools/rxjs/** @angular/fw-dev-infra +/tools/source-map-test/** @angular/fw-dev-infra +/tools/symbol-extractor/** @angular/fw-dev-infra +/tools/testing/** @angular/fw-dev-infra +/tools/ts-api-guardian/** @angular/fw-dev-infra +/tools/tslint/** @angular/fw-dev-infra +/tools/validate-commit-message/** @angular/fw-dev-infra +/tools/yarn/** @angular/fw-dev-infra +/tools/* *.bzl @angular/fw-dev-infra + + # ================================================ # Material CI # ================================================ -/tools/material-ci/** @angular/fw-core @angular/fw-dev-infra +/tools/material-ci/** @angular/fw-core @angular/framework-global-approvers + # ================================================ @@ -761,6 +894,10 @@ testing/** @angular/fw-test # ================================================ /tools/public_api_guard/** @angular/fw-public-api +/aio/content/guide/glossary.md @angular/fw-public-api +/aio/content/guide/styleguide.md @angular/fw-public-api +/aio/content/examples/styleguide/** @angular/fw-public-api +/aio/content/images/guide/styleguide/** @angular/fw-public-api diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md index 14f87d1298..1424df5367 100644 --- a/.github/ISSUE_TEMPLATE/1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/1-bug-report.md @@ -37,7 +37,9 @@ Please create and share minimal reproduction of the issue starting with this tem https://stackblitz.com/...

Highlight me!

- +

Highlighted in yellow

@@ -11,4 +11,4 @@

Highlighted with parent component's color

- + \ No newline at end of file diff --git a/aio/content/examples/attribute-directives/src/app/app.component.avoid.html b/aio/content/examples/attribute-directives/src/app/app.component.avoid.html new file mode 100644 index 0000000000..464e8f30e5 --- /dev/null +++ b/aio/content/examples/attribute-directives/src/app/app.component.avoid.html @@ -0,0 +1,3 @@ + +

This is invalid

+ \ No newline at end of file diff --git a/aio/content/examples/built-in-template-functions/e2e/src/app.e2e-spec.ts b/aio/content/examples/built-in-template-functions/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..79c9f38d1c --- /dev/null +++ b/aio/content/examples/built-in-template-functions/e2e/src/app.e2e-spec.ts @@ -0,0 +1,20 @@ +'use strict'; // necessary for es6 output in node + +import { browser, element, by } from 'protractor'; + +describe('Built Template Functions Example', function () { + beforeAll(function () { + browser.get(''); + }); + + it('should have title Built-in Template Functions', function () { + let title = element.all(by.css('h1')).get(0); + expect(title.getText()).toEqual('Built-in Template Functions'); + }); + + it('should display $any( ) in h2', function () { + let header = element(by.css('h2')); + expect(header.getText()).toContain('$any( )'); + }); + +}); diff --git a/aio/content/examples/ngmodule-faq/example-config.json b/aio/content/examples/built-in-template-functions/example-config.json similarity index 100% rename from aio/content/examples/ngmodule-faq/example-config.json rename to aio/content/examples/built-in-template-functions/example-config.json diff --git a/aio/content/examples/built-in-template-functions/src/app/app.component.css b/aio/content/examples/built-in-template-functions/src/app/app.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/built-in-template-functions/src/app/app.component.html b/aio/content/examples/built-in-template-functions/src/app/app.component.html new file mode 100644 index 0000000000..0a10c023e4 --- /dev/null +++ b/aio/content/examples/built-in-template-functions/src/app/app.component.html @@ -0,0 +1,15 @@ +

{{title}}

+ +

$any( ) type cast function and an undeclared member

+ +

There is no such member as bestByDate in the following two examples, so nothing renders:

+ +

The item's undeclared best by date is: {{$any(item).bestByDate}}

+ + +

using this

+ +

The item's undeclared best by date is: {{$any(this).bestByDate}}

+ + + diff --git a/aio/content/examples/built-in-template-functions/src/app/app.component.spec.ts b/aio/content/examples/built-in-template-functions/src/app/app.component.spec.ts new file mode 100644 index 0000000000..31b39079ce --- /dev/null +++ b/aio/content/examples/built-in-template-functions/src/app/app.component.spec.ts @@ -0,0 +1,16 @@ +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(); + })); +}); diff --git a/aio/content/examples/built-in-template-functions/src/app/app.component.ts b/aio/content/examples/built-in-template-functions/src/app/app.component.ts new file mode 100644 index 0000000000..50ac8c7896 --- /dev/null +++ b/aio/content/examples/built-in-template-functions/src/app/app.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'Built-in Template Functions'; + + item = { + name : 'Telephone', + origin : 'Sweden', + price: 98 + }; +} diff --git a/aio/content/examples/built-in-template-functions/src/app/app.module.ts b/aio/content/examples/built-in-template-functions/src/app/app.module.ts new file mode 100644 index 0000000000..926975afe8 --- /dev/null +++ b/aio/content/examples/built-in-template-functions/src/app/app.module.ts @@ -0,0 +1,18 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; + + +import { AppComponent } from './app.component'; + + +@NgModule({ + declarations: [ + AppComponent + ], + imports: [ + BrowserModule + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/aio/content/examples/built-in-template-functions/src/index.html b/aio/content/examples/built-in-template-functions/src/index.html new file mode 100644 index 0000000000..ec5292df8c --- /dev/null +++ b/aio/content/examples/built-in-template-functions/src/index.html @@ -0,0 +1,14 @@ + + + + + Built-in Template Functions Example + + + + + + + Loading... + + diff --git a/aio/content/examples/ngmodule-faq/src/main.ts b/aio/content/examples/built-in-template-functions/src/main.ts similarity index 96% rename from aio/content/examples/ngmodule-faq/src/main.ts rename to aio/content/examples/built-in-template-functions/src/main.ts index 0740658908..a9ca1caf8c 100644 --- a/aio/content/examples/ngmodule-faq/src/main.ts +++ b/aio/content/examples/built-in-template-functions/src/main.ts @@ -1,4 +1,3 @@ -// #docregion import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; diff --git a/aio/content/examples/built-in-template-functions/stackblitz.json b/aio/content/examples/built-in-template-functions/stackblitz.json new file mode 100644 index 0000000000..d77c2c273f --- /dev/null +++ b/aio/content/examples/built-in-template-functions/stackblitz.json @@ -0,0 +1,10 @@ +{ + "description": "Built-in Template Functions", + "files": [ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[1,2].*" + ], + "file": "src/app/app.component.ts", + "tags": ["Built-in Template Functions"] +} diff --git a/aio/content/examples/component-interaction/example-config.json b/aio/content/examples/component-interaction/example-config.json index b93fc9e8da..054f3cca48 100644 --- a/aio/content/examples/component-interaction/example-config.json +++ b/aio/content/examples/component-interaction/example-config.json @@ -4,7 +4,8 @@ "cmd": "yarn", "args": [ "e2e", - "--no-webdriver-update" + "--no-webdriver-update", + "--port={PORT}" ] } ] diff --git a/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts b/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts index 07ab501be7..00dd11868c 100644 --- a/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts @@ -73,8 +73,12 @@ describe('Dependency Injection Cookbook', function () { let yellow = 'rgba(255, 255, 0, 1)'; expect(target.getCssValue('background-color')).not.toEqual(yellow); - browser.actions().mouseMove(target.getWebElement()).perform(); - expect(target.getCssValue('background-color')).toEqual(yellow); + + browser.actions().mouseMove(target).perform(); + + // Wait for up to 2s for the background color to be updated, + // to account for slow environments (e.g. CI). + browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000); }); describe('in Parent Finder', function () { diff --git a/aio/content/examples/dependency-injection/example-config.json b/aio/content/examples/dependency-injection/example-config.json index b93fc9e8da..054f3cca48 100644 --- a/aio/content/examples/dependency-injection/example-config.json +++ b/aio/content/examples/dependency-injection/example-config.json @@ -4,7 +4,8 @@ "cmd": "yarn", "args": [ "e2e", - "--no-webdriver-update" + "--no-webdriver-update", + "--port={PORT}" ] } ] diff --git a/aio/content/examples/getting-started-v0/e2e/src/app.e2e-spec.ts b/aio/content/examples/getting-started-v0/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..09aa3abfed --- /dev/null +++ b/aio/content/examples/getting-started-v0/e2e/src/app.e2e-spec.ts @@ -0,0 +1,21 @@ +'use strict'; // necessary for es6 output in node + +import { browser, element, by } from 'protractor'; + +describe('Getting Started V0', () => { + beforeEach(() => { + return browser.get('/'); + }); + + it('should display "My Store" in the top bar', async() => { + const title = await element(by.css('app-root app-top-bar h1')).getText(); + + expect(title).toEqual('My Store'); + }); + + it('should display "Products" on the homepage', async() => { + const title = await element(by.css('app-root app-product-list h2')).getText(); + + expect(title).toEqual('Products'); + }); +}); diff --git a/aio/content/examples/getting-started-v0/example-config.json b/aio/content/examples/getting-started-v0/example-config.json new file mode 100644 index 0000000000..32a5ae69cc --- /dev/null +++ b/aio/content/examples/getting-started-v0/example-config.json @@ -0,0 +1,4 @@ +{ + "useCommonBoilerplate": false, + "projectType": "getting-started" +} \ No newline at end of file diff --git a/aio/content/examples/getting-started-v0/src/app/app.component.css b/aio/content/examples/getting-started-v0/src/app/app.component.css new file mode 100644 index 0000000000..b7ef084c56 --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/app.component.css @@ -0,0 +1,3 @@ +p { + font-family: Lato; +} \ No newline at end of file diff --git a/aio/content/examples/getting-started-v0/src/app/app.component.html b/aio/content/examples/getting-started-v0/src/app/app.component.html new file mode 100644 index 0000000000..14109efcf1 --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/app.component.html @@ -0,0 +1,5 @@ + + +
+ +
\ No newline at end of file diff --git a/aio/content/examples/getting-started-v0/src/app/app.component.ts b/aio/content/examples/getting-started-v0/src/app/app.component.ts new file mode 100644 index 0000000000..e98ec2a712 --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/app.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: [ './app.component.css' ] +}) +export class AppComponent {} diff --git a/aio/content/examples/getting-started-v0/src/app/app.module.ts b/aio/content/examples/getting-started-v0/src/app/app.module.ts new file mode 100644 index 0000000000..1dbba86431 --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/app.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { AppComponent } from './app.component'; +import { TopBarComponent } from './top-bar/top-bar.component'; +import { ProductListComponent } from './product-list/product-list.component'; + +@NgModule({ + imports: [ + BrowserModule, + ReactiveFormsModule, + RouterModule.forRoot([ + { path: '', component: ProductListComponent }, + ]) + ], + declarations: [ + AppComponent, + TopBarComponent, + ProductListComponent + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } diff --git a/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.css b/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.html b/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.html new file mode 100644 index 0000000000..1007667e7b --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.html @@ -0,0 +1 @@ +

Products

diff --git a/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.ts b/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.ts new file mode 100644 index 0000000000..af123522ea --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/product-list/product-list.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; + +import { products } from '../products'; + +@Component({ + selector: 'app-product-list', + templateUrl: './product-list.component.html', + styleUrls: ['./product-list.component.css'] +}) +export class ProductListComponent { + products = products; + + share() { + window.alert('The product has been shared!'); + } +} diff --git a/aio/content/examples/getting-started-v0/src/app/products.ts b/aio/content/examples/getting-started-v0/src/app/products.ts new file mode 100644 index 0000000000..da94ebbdea --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/products.ts @@ -0,0 +1,17 @@ +export const products = [ + { + name: 'Phone XL', + price: 799, + description: 'A large phone with one of the best screens' + }, + { + name: 'Phone Mini', + price: 699, + description: 'A great phone with one of the best cameras' + }, + { + name: 'Phone Standard', + price: 299, + description: '' + } +]; diff --git a/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.css b/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.html b/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.html new file mode 100644 index 0000000000..dafaed6350 --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.html @@ -0,0 +1,5 @@ + +

My Store

+
+ +shopping_cartCheckout \ No newline at end of file diff --git a/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.ts b/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.ts new file mode 100644 index 0000000000..e531b08799 --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/app/top-bar/top-bar.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-top-bar', + templateUrl: './top-bar.component.html', + styleUrls: ['./top-bar.component.css'] +}) +export class TopBarComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/aio/content/examples/getting-started-v0/src/assets/shipping.json b/aio/content/examples/getting-started-v0/src/assets/shipping.json new file mode 100644 index 0000000000..94db54c96d --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/assets/shipping.json @@ -0,0 +1,14 @@ +[ + { + "type": "Overnight", + "price": 25.99 + }, + { + "type": "2-Day", + "price": 9.99 + }, + { + "type": "Postal", + "price": 2.99 + } +] \ No newline at end of file diff --git a/aio/content/examples/getting-started-v0/src/index.html b/aio/content/examples/getting-started-v0/src/index.html new file mode 100644 index 0000000000..d7c3a0a6d6 --- /dev/null +++ b/aio/content/examples/getting-started-v0/src/index.html @@ -0,0 +1,18 @@ + + + + + Angular Getting Started + + + + + + + + + + diff --git a/aio/content/examples/ngmodule-faq/src/main.2.ts b/aio/content/examples/getting-started-v0/src/main.ts similarity index 85% rename from aio/content/examples/ngmodule-faq/src/main.2.ts rename to aio/content/examples/getting-started-v0/src/main.ts index 4349ed7403..61c5f24eca 100644 --- a/aio/content/examples/ngmodule-faq/src/main.2.ts +++ b/aio/content/examples/getting-started-v0/src/main.ts @@ -1,9 +1,9 @@ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module.2'; import { environment } from './environments/environment'; +import { AppModule } from './app/app.module'; + if (environment.production) { enableProdMode(); } diff --git a/aio/content/examples/getting-started-v0/stackblitz.json b/aio/content/examples/getting-started-v0/stackblitz.json new file mode 100644 index 0000000000..a1f0944d46 --- /dev/null +++ b/aio/content/examples/getting-started-v0/stackblitz.json @@ -0,0 +1,9 @@ +{ + "description": "Getting Started", + "files":[ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[0-9].*" + ], + "tags": ["Angular", "getting started", "tutorial"] +} diff --git a/aio/content/examples/getting-started/e2e/src/app.e2e-spec.ts b/aio/content/examples/getting-started/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..e2a2bd2db0 --- /dev/null +++ b/aio/content/examples/getting-started/e2e/src/app.e2e-spec.ts @@ -0,0 +1,118 @@ +'use strict'; // necessary for es6 output in node + +import { browser, element, by, ExpectedConditions as EC, logging, ElementFinder, ElementArrayFinder } from 'protractor'; + +describe('Getting Started', () => { + const pageElements = { + topBarHeader: element(by.css('app-root app-top-bar h1')), + topBarLinks: element(by.css('app-root app-top-bar a')), + topBarCheckoutLink: element(by.cssContainingText('app-root app-top-bar a', 'Checkout')), + productListHeader: element(by.css('app-root app-product-list h2')), + productListItems: element.all(by.css('app-root app-product-list h3')), + productListLinks: element.all(by.css('app-root app-product-list a')), + productDetailsPage: element(by.css('app-root app-product-details div')), + cartPage: element(by.css('app-root app-cart')) + }; + + describe('General', () => { + beforeAll(async() => { + await browser.get('/'); + }); + + it('should display "My Store"', async() => { + const title = await pageElements.topBarHeader.getText(); + + expect(title).toEqual('My Store'); + }); + + it('should display "Products" on the homepage', async() => { + const title = await pageElements.productListHeader.getText(); + + expect(title).toEqual('Products'); + }); + }); + + describe('Product List', () => { + beforeAll(async() => { + await browser.get('/'); + }); + + it('should display 3 items', async() => { + const products = await pageElements.productListItems; + + expect(products.length).toEqual(3); + }); + }); + + describe('Product Details', () => { + beforeEach(async() => { + await browser.get('/'); + }); + + it('should display information for a product', async() => { + await pageElements.productListLinks.get(0).click(); + + const product = pageElements.productDetailsPage; + const productHeader = await product.element(by.css('h3')).getText(); + const productPrice = await product.element(by.css('h4')).getText(); + const productDescription = await product.element(by.css('p')).getText(); + + expect(await product.isDisplayed()).toBeTruthy(); + expect(productHeader).toBe('Phone XL'); + expect(productPrice).toBe('$799.00'); + expect(productDescription).toBe('A large phone with one of the best screens'); + }); + + it('should add the product to the cart', async() => { + await pageElements.productListLinks.get(0).click(); + + const product = pageElements.productDetailsPage; + const buyButton = await product.element(by.css('button')); + const checkoutLink = pageElements.topBarCheckoutLink; + + await buyButton.click(); + await browser.wait(EC.alertIsPresent(), 1000); + await browser.switchTo().alert().accept(); + await checkoutLink.click(); + + const cartItems = await element.all(by.css('app-root app-cart div.cart-item')); + expect(cartItems.length).toBe(1); + }); + }); + + describe('Cart', () => { + + beforeEach(async() => { + await browser.get('/'); + }); + + it('should go through the checkout process', async() => { + await pageElements.productListLinks.get(0).click(); + + const checkoutLink = pageElements.topBarCheckoutLink; + const productDetailsPage = pageElements.productDetailsPage; + const buyButton = await productDetailsPage.element(by.css('button')); + + const cartPage = pageElements.cartPage; + const inputFields = cartPage.all(by.css('form input')); + + const purchaseButton = await cartPage.element(by.css('button')); + const nameField = inputFields.get(0); + const addressField = inputFields.get(1); + + await buyButton.click(); + await browser.wait(EC.alertIsPresent(), 1000); + await browser.switchTo().alert().accept(); + await checkoutLink.click(); + + await nameField.sendKeys('Customer'); + await addressField.sendKeys('Address'); + await purchaseButton.click(); + + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + const cartMessages = logs.filter(({ message }) => message.includes('Your order has been submitted')); + + expect(cartMessages.length).toBe(1); + }); + }); +}); diff --git a/aio/content/examples/getting-started/example-config.json b/aio/content/examples/getting-started/example-config.json new file mode 100644 index 0000000000..32a5ae69cc --- /dev/null +++ b/aio/content/examples/getting-started/example-config.json @@ -0,0 +1,4 @@ +{ + "useCommonBoilerplate": false, + "projectType": "getting-started" +} \ No newline at end of file diff --git a/aio/content/examples/getting-started/src/app/app.component.css b/aio/content/examples/getting-started/src/app/app.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started/src/app/app.component.html b/aio/content/examples/getting-started/src/app/app.component.html new file mode 100644 index 0000000000..14109efcf1 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/app.component.html @@ -0,0 +1,5 @@ + + +
+ +
\ No newline at end of file diff --git a/aio/content/examples/ngmodule-faq/src/app/app.component.0.ts b/aio/content/examples/getting-started/src/app/app.component.ts similarity index 59% rename from aio/content/examples/ngmodule-faq/src/app/app.component.0.ts rename to aio/content/examples/getting-started/src/app/app.component.ts index 9aac868764..68478a8623 100644 --- a/aio/content/examples/ngmodule-faq/src/app/app.component.0.ts +++ b/aio/content/examples/getting-started/src/app/app.component.ts @@ -1,10 +1,9 @@ -// #docregion import { Component } from '@angular/core'; @Component({ selector: 'app-root', - template: '

{{title}}

', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] }) export class AppComponent { - title = 'Angular Modules'; } diff --git a/aio/content/examples/getting-started/src/app/app.module.ts b/aio/content/examples/getting-started/src/app/app.module.ts new file mode 100644 index 0000000000..fd49de191f --- /dev/null +++ b/aio/content/examples/getting-started/src/app/app.module.ts @@ -0,0 +1,55 @@ +// #docplaster +// #docregion http-client-module-import, http-client-module +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { HttpClientModule } from '@angular/common/http'; +// #enddocregion http-client-module-import +import { ReactiveFormsModule } from '@angular/forms'; + +import { AppComponent } from './app.component'; +import { TopBarComponent } from './top-bar/top-bar.component'; +import { ProductListComponent } from './product-list/product-list.component'; +import { ProductAlertsComponent } from './product-alerts/product-alerts.component'; +import { ProductDetailsComponent } from './product-details/product-details.component'; +// #enddocregion http-client-module +import { CartComponent } from './cart/cart.component'; +import { ShippingComponent } from './shipping/shipping.component'; + +// #docregion product-details-route, http-client-module, shipping-route, cart-route + +@NgModule({ + imports: [ + BrowserModule, + // #enddocregion product-details-route, cart-route + HttpClientModule, + // #docregion product-details-route, cart-route + ReactiveFormsModule, + RouterModule.forRoot([ + { path: '', component: ProductListComponent }, + { path: 'products/:productId', component: ProductDetailsComponent }, +// #enddocregion product-details-route + { path: 'cart', component: CartComponent }, +// #enddocregion cart-route, http-client-module + { path: 'shipping', component: ShippingComponent }, +// #enddocregion shipping-route +// #docregion product-details-route, http-client-module, shipping-route, cart-route + ]) + ], + // #enddocregion product-details-route, cart-route + declarations: [ + AppComponent, + TopBarComponent, + ProductListComponent, + ProductAlertsComponent, + ProductDetailsComponent, + CartComponent, +// #enddocregion http-client-module + ShippingComponent +// #docregion http-client-module + ], + bootstrap: [ + AppComponent + ] +}) +export class AppModule { } diff --git a/aio/content/examples/getting-started/src/app/cart.service.1.ts b/aio/content/examples/getting-started/src/app/cart.service.1.ts new file mode 100644 index 0000000000..40d52da108 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart.service.1.ts @@ -0,0 +1,13 @@ +// #docplaster +// #docregion +import { Injectable } from '@angular/core'; + +// #docregion v1 +@Injectable({ + providedIn: 'root' +}) +export class CartService { + + constructor() {} + +} diff --git a/aio/content/examples/getting-started/src/app/cart.service.ts b/aio/content/examples/getting-started/src/app/cart.service.ts new file mode 100644 index 0000000000..b28b22f351 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart.service.ts @@ -0,0 +1,39 @@ +// #docplaster +// #docregion import-http +import { Injectable } from '@angular/core'; + +import { HttpClient } from '@angular/common/http'; +// #enddocregion import-http +@Injectable({ + providedIn: 'root' +}) +// #docregion props, methods, inject-http, get-shipping +export class CartService { + items = []; +// #enddocregion props, methods + + constructor( + private http: HttpClient + ) {} +// #enddocregion inject-http +// #docregion methods + + addToCart(product) { + this.items.push(product); + } + + getItems() { + return this.items; + } + + clearCart() { + this.items = []; + return this.items; + } +// #enddocregion methods + + getShippingPrices() { + return this.http.get('/assets/shipping.json'); + } +// #docregion props, methods, inject-http +} diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.1.ts b/aio/content/examples/getting-started/src/app/cart/cart.component.1.ts new file mode 100644 index 0000000000..c9276d4d36 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart/cart.component.1.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-cart', + templateUrl: './cart.component.html', + styleUrls: ['./cart.component.css'] +}) +export class CartComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.2.html b/aio/content/examples/getting-started/src/app/cart/cart.component.2.html new file mode 100644 index 0000000000..050618d664 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart/cart.component.2.html @@ -0,0 +1,15 @@ + + +

Cart

+ + +

+ Shipping Prices +

+ + +
+ {{ item.name }} + {{ item.price | currency }} +
+ \ No newline at end of file diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.2.ts b/aio/content/examples/getting-started/src/app/cart/cart.component.2.ts new file mode 100644 index 0000000000..8615c5a391 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart/cart.component.2.ts @@ -0,0 +1,21 @@ +// #docplaster +// #docregion imports +import { Component } from '@angular/core'; +import { CartService } from '../cart.service'; +// #enddocregion imports + +@Component({ + selector: 'app-cart', + templateUrl: './cart.component.html', + styleUrls: ['./cart.component.css'] +}) +// #docregion inject-cart, items, submit +export class CartComponent { +// #enddocregion inject-cart + items; +// #docregion inject-cart + + constructor( + private cartService: CartService + ) { } +} diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.3.html b/aio/content/examples/getting-started/src/app/cart/cart.component.3.html new file mode 100644 index 0000000000..11f860d66d --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart/cart.component.3.html @@ -0,0 +1,19 @@ + +

Cart

+ +

+ Shipping Prices +

+ +
+ {{ item.name }} + {{ item.price | currency }} +
+ + +
+ + + +
+ diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.3.ts b/aio/content/examples/getting-started/src/app/cart/cart.component.3.ts new file mode 100644 index 0000000000..9f7847ab0a --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart/cart.component.3.ts @@ -0,0 +1,21 @@ +// #docplaster +// #docregion imports +import { Component } from '@angular/core'; +import { CartService } from '../cart.service'; +// #enddocregion imports + +@Component({ + selector: 'app-cart', + templateUrl: './cart.component.html', + styleUrls: ['./cart.component.css'] +}) +// #docregion props-services, submit +export class CartComponent { + items; + + constructor( + private cartService: CartService + ) { + this.items = this.cartService.getItems(); + } +} diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.css b/aio/content/examples/getting-started/src/app/cart/cart.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.html b/aio/content/examples/getting-started/src/app/cart/cart.component.html new file mode 100644 index 0000000000..060e9d8823 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart/cart.component.html @@ -0,0 +1,31 @@ + + +

Cart

+ +

+ Shipping Prices +

+ +
+ {{ item.name }} + {{ item.price | currency }} +
+ + +
+ + +
+ + +
+ +
+ + +
+ + + + +
diff --git a/aio/content/examples/getting-started/src/app/cart/cart.component.ts b/aio/content/examples/getting-started/src/app/cart/cart.component.ts new file mode 100644 index 0000000000..cf7f31a4f4 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/cart/cart.component.ts @@ -0,0 +1,46 @@ +// #docplaster +// #docregion imports +import { Component } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; + +import { CartService } from '../cart.service'; +// #enddocregion imports + +@Component({ + selector: 'app-cart', + templateUrl: './cart.component.html', + styleUrls: ['./cart.component.css'] +}) +// #docregion props-services, submit, inject-form-builder, checkout-form, checkout-form-group +export class CartComponent { + items; +// #enddocregion inject-form-builder + checkoutForm; +// #enddocregion checkout-form +// #docregion inject-form-builder + + constructor( + private cartService: CartService, + private formBuilder: FormBuilder, + ) { +// #enddocregion inject-form-builder + this.items = this.cartService.getItems(); + + this.checkoutForm = this.formBuilder.group({ + name: '', + address: '' + }); +// #docregion inject-form-builder + } +// #enddocregion inject-form-builder, checkout-form-group + + // #enddocregion props-services + onSubmit(customerData) { + // Process checkout data here + console.warn('Your order has been submitted', customerData); + + this.items = this.cartService.clearCart(); + this.checkoutForm.reset(); + } +// #docregion props-services, inject-form-builder, checkout-form, checkout-form-group +} diff --git a/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.1.html b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.1.html new file mode 100644 index 0000000000..7ed54e8511 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.1.html @@ -0,0 +1,3 @@ +

+ +

diff --git a/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.1.ts b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.1.ts new file mode 100644 index 0000000000..a92b624261 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.1.ts @@ -0,0 +1,24 @@ +// #docplaster +// #docregion as-generated, imports +import { Component, OnInit } from '@angular/core'; +// #enddocregion as-generated +import { Input } from '@angular/core'; +// #enddocregion imports +// #docregion as-generated + +@Component({ + selector: 'app-product-alerts', + templateUrl: './product-alerts.component.html', + styleUrls: ['./product-alerts.component.css'] +}) +// #docregion input-decorator +export class ProductAlertsComponent implements OnInit { +// #enddocregion as-generated + @Input() product; +// #docregion as-generated + constructor() { } + + ngOnInit() { + } + +} diff --git a/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.css b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.html b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.html new file mode 100644 index 0000000000..bcf6553d99 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.html @@ -0,0 +1,3 @@ +

+ +

diff --git a/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.ts b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.ts new file mode 100644 index 0000000000..42b4d59e0a --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-alerts/product-alerts.component.ts @@ -0,0 +1,17 @@ +// #docplaster +// #docregion imports +import { Component } from '@angular/core'; +import { Input } from '@angular/core'; +import { Output, EventEmitter } from '@angular/core'; +// #enddocregion imports + +@Component({ + selector: 'app-product-alerts', + templateUrl: './product-alerts.component.html', + styleUrls: ['./product-alerts.component.css'] +}) +// #docregion input-output +export class ProductAlertsComponent { + @Input() product; + @Output() notify = new EventEmitter(); +} diff --git a/aio/content/examples/getting-started/src/app/product-details/product-details.component.1.ts b/aio/content/examples/getting-started/src/app/product-details/product-details.component.1.ts new file mode 100644 index 0000000000..ec22944f7d --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-details/product-details.component.1.ts @@ -0,0 +1,31 @@ +// #docplaster +// #docregion imports +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { products } from '../products'; +// #enddocregion imports + +@Component({ + selector: 'app-product-details', + templateUrl: './product-details.component.html', + styleUrls: ['./product-details.component.css'] +}) +// #docregion props-methods, add-to-cart +export class ProductDetailsComponent implements OnInit { + product; + + constructor( + private route: ActivatedRoute, + ) { } + + // #enddocregion props-methods + // #docregion get-product + ngOnInit() { + this.route.paramMap.subscribe(params => { + this.product = products[+params.get('productId')]; + }); + } + // #enddocregion get-product + // #docregion props-methods +} diff --git a/aio/content/examples/getting-started/src/app/product-details/product-details.component.css b/aio/content/examples/getting-started/src/app/product-details/product-details.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started/src/app/product-details/product-details.component.html b/aio/content/examples/getting-started/src/app/product-details/product-details.component.html new file mode 100644 index 0000000000..5b4a5452d2 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-details/product-details.component.html @@ -0,0 +1,13 @@ + + +

Product Details

+ +
+

{{ product.name }}

+

{{ product.price | currency }}

+

{{ product.description }}

+ + + + +
diff --git a/aio/content/examples/getting-started/src/app/product-details/product-details.component.ts b/aio/content/examples/getting-started/src/app/product-details/product-details.component.ts new file mode 100644 index 0000000000..0f53e18b04 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-details/product-details.component.ts @@ -0,0 +1,46 @@ +// #docplaster +// #docregion imports, cart-service +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { products } from '../products'; +// #enddocregion imports +import { CartService } from '../cart.service'; +// #enddocregion cart-service + +@Component({ + selector: 'app-product-details', + templateUrl: './product-details.component.html', + styleUrls: ['./product-details.component.css'] +}) +// #docregion props-methods, get-product, inject-cart-service, add-to-cart +export class ProductDetailsComponent implements OnInit { +// #enddocregion add-to-cart, get-product, inject-cart-service + product; + +// #docregion inject-cart-service + constructor( + private route: ActivatedRoute, +// #enddocregion props-methods + private cartService: CartService +// #docregion props-methods + ) { } +// #enddocregion inject-cart-service + +// #docregion get-product + ngOnInit() { +// #enddocregion props-methods + this.route.paramMap.subscribe(params => { + this.product = products[+params.get('productId')]; + }); +// #docregion props-methods + } + +// #enddocregion props-methods, get-product +// #docregion add-to-cart + addToCart(product) { + window.alert('Your product has been added to the cart!'); + this.cartService.addToCart(product); + } +// #docregion props-methods, get-product, inject-cart-service +} diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.1.html b/aio/content/examples/getting-started/src/app/product-list/product-list.component.1.html new file mode 100644 index 0000000000..1007667e7b --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.1.html @@ -0,0 +1 @@ +

Products

diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.1.ts b/aio/content/examples/getting-started/src/app/product-list/product-list.component.1.ts new file mode 100644 index 0000000000..f70d700ab1 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.1.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import { products } from '../products'; + +@Component({ + selector: 'app-product-list', + templateUrl: './product-list.component.html', + styleUrls: ['./product-list.component.css'] +}) +export class ProductListComponent { + products = products; + + share() { + window.alert('The product has been shared!'); + } +} diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.2.html b/aio/content/examples/getting-started/src/app/product-list/product-list.component.2.html new file mode 100644 index 0000000000..4849729b49 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.2.html @@ -0,0 +1,20 @@ + + +

Products

+ +
+ + +

+ + + + {{ product.name }} + + + +

+ + +
+ diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.3.html b/aio/content/examples/getting-started/src/app/product-list/product-list.component.3.html new file mode 100644 index 0000000000..3e957f5740 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.3.html @@ -0,0 +1,15 @@ +

Products

+ +
+ +

+ + {{ product.name }} + +

+ +

+ Description: {{ product.description }} +

+ +
diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.4.html b/aio/content/examples/getting-started/src/app/product-list/product-list.component.4.html new file mode 100644 index 0000000000..1e2781d8d2 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.4.html @@ -0,0 +1,19 @@ +

Products

+ +
+ +

+ + {{ product.name }} + +

+ +

+ Description: {{ product.description }} +

+ + + +
diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.5.html b/aio/content/examples/getting-started/src/app/product-list/product-list.component.5.html new file mode 100644 index 0000000000..131c58543e --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.5.html @@ -0,0 +1,28 @@ +

Products

+ +
+ + +

+ + {{ product.name }} + +

+ + +

+ Description: {{ product.description }} +

+ + + + + + + + +
+ diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.6.html b/aio/content/examples/getting-started/src/app/product-list/product-list.component.6.html new file mode 100644 index 0000000000..47ed78e7e1 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.6.html @@ -0,0 +1,27 @@ +

Products

+ +
+ +

+ + {{ product.name }} + +

+ +

+ Description: {{ product.description }} +

+ + + + + + + + +
+ diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.css b/aio/content/examples/getting-started/src/app/product-list/product-list.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.html b/aio/content/examples/getting-started/src/app/product-list/product-list.component.html new file mode 100644 index 0000000000..f1b86df848 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.html @@ -0,0 +1,26 @@ +

Products

+ + +
+ +

+ + {{ product.name }} + +

+ +

+ Description: {{ product.description }} +

+ + + + + + +
+ diff --git a/aio/content/examples/getting-started/src/app/product-list/product-list.component.ts b/aio/content/examples/getting-started/src/app/product-list/product-list.component.ts new file mode 100644 index 0000000000..4a6c616738 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/product-list/product-list.component.ts @@ -0,0 +1,21 @@ +import { Component } from '@angular/core'; + +import { products } from '../products'; + +@Component({ + selector: 'app-product-list', + templateUrl: './product-list.component.html', + styleUrls: ['./product-list.component.css'] +}) +// #docregion on-notify +export class ProductListComponent { + products = products; + + share() { + window.alert('The product has been shared!'); + } + + onNotify() { + window.alert('You will be notified when the product goes on sale'); + } +} diff --git a/aio/content/examples/getting-started/src/app/products.ts b/aio/content/examples/getting-started/src/app/products.ts new file mode 100644 index 0000000000..da94ebbdea --- /dev/null +++ b/aio/content/examples/getting-started/src/app/products.ts @@ -0,0 +1,17 @@ +export const products = [ + { + name: 'Phone XL', + price: 799, + description: 'A large phone with one of the best screens' + }, + { + name: 'Phone Mini', + price: 699, + description: 'A great phone with one of the best cameras' + }, + { + name: 'Phone Standard', + price: 299, + description: '' + } +]; diff --git a/aio/content/examples/getting-started/src/app/shipping/shipping.component.1.ts b/aio/content/examples/getting-started/src/app/shipping/shipping.component.1.ts new file mode 100644 index 0000000000..3bbacae6bd --- /dev/null +++ b/aio/content/examples/getting-started/src/app/shipping/shipping.component.1.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-shipping', + templateUrl: './shipping.component.html', + styleUrls: ['./shipping.component.css'] +}) +export class ShippingComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/aio/content/examples/getting-started/src/app/shipping/shipping.component.css b/aio/content/examples/getting-started/src/app/shipping/shipping.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started/src/app/shipping/shipping.component.html b/aio/content/examples/getting-started/src/app/shipping/shipping.component.html new file mode 100644 index 0000000000..d4566bc318 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/shipping/shipping.component.html @@ -0,0 +1,6 @@ +

Shipping Prices

+ +
+ {{ shipping.type }} + {{ shipping.price | currency }} +
\ No newline at end of file diff --git a/aio/content/examples/getting-started/src/app/shipping/shipping.component.ts b/aio/content/examples/getting-started/src/app/shipping/shipping.component.ts new file mode 100644 index 0000000000..4a86d5c664 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/shipping/shipping.component.ts @@ -0,0 +1,26 @@ +// #docplaster +// #docregion imports +import { Component } from '@angular/core'; + +import { CartService } from '../cart.service'; +// #enddocregion + +@Component({ + selector: 'app-shipping', + templateUrl: './shipping.component.html', + styleUrls: ['./shipping.component.css'] +}) +// #docregion props, ctor +export class ShippingComponent { + shippingCosts; +// #enddocregion props + +// #docregion inject-cart-service + constructor(private cartService: CartService) { +// #enddocregion inject-cart-service + this.shippingCosts = this.cartService.getShippingPrices(); +// #docregion inject-cart-service + } +// #enddocregion inject-cart-service +// #docregion props +} diff --git a/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.1.html b/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.1.html new file mode 100644 index 0000000000..82f53e5f1e --- /dev/null +++ b/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.1.html @@ -0,0 +1,7 @@ + +

My Store

+
+ + + shopping_cartCheckout + diff --git a/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.css b/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.html b/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.html new file mode 100644 index 0000000000..f96b490311 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.html @@ -0,0 +1,7 @@ + +

My Store

+
+ + + shopping_cartCheckout + diff --git a/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.ts b/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.ts new file mode 100644 index 0000000000..12fe8c9488 --- /dev/null +++ b/aio/content/examples/getting-started/src/app/top-bar/top-bar.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-top-bar', + templateUrl: './top-bar.component.html', + styleUrls: ['./top-bar.component.css'] +}) +export class TopBarComponent { + +} diff --git a/aio/content/examples/getting-started/src/assets/shipping.json b/aio/content/examples/getting-started/src/assets/shipping.json new file mode 100644 index 0000000000..94db54c96d --- /dev/null +++ b/aio/content/examples/getting-started/src/assets/shipping.json @@ -0,0 +1,14 @@ +[ + { + "type": "Overnight", + "price": 25.99 + }, + { + "type": "2-Day", + "price": 9.99 + }, + { + "type": "Postal", + "price": 2.99 + } +] \ No newline at end of file diff --git a/aio/content/examples/getting-started/src/index.html b/aio/content/examples/getting-started/src/index.html new file mode 100644 index 0000000000..d7c3a0a6d6 --- /dev/null +++ b/aio/content/examples/getting-started/src/index.html @@ -0,0 +1,18 @@ + + + + + Angular Getting Started + + + + + + + + + + diff --git a/aio/content/examples/ngmodule-faq/src/main.0.ts b/aio/content/examples/getting-started/src/main.ts similarity index 63% rename from aio/content/examples/ngmodule-faq/src/main.0.ts rename to aio/content/examples/getting-started/src/main.ts index ed431adb1a..c7b673cf44 100644 --- a/aio/content/examples/ngmodule-faq/src/main.0.ts +++ b/aio/content/examples/getting-started/src/main.ts @@ -1,11 +1,12 @@ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module.0'; +import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/aio/content/examples/getting-started/stackblitz.json b/aio/content/examples/getting-started/stackblitz.json new file mode 100644 index 0000000000..a1f0944d46 --- /dev/null +++ b/aio/content/examples/getting-started/stackblitz.json @@ -0,0 +1,9 @@ +{ + "description": "Getting Started", + "files":[ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[0-9].*" + ], + "tags": ["Angular", "getting started", "tutorial"] +} diff --git a/aio/content/examples/http/src/browser-test-shim.js b/aio/content/examples/http/src/browser-test-shim.js index 6a7ce2698b..1b0e954aac 100644 --- a/aio/content/examples/http/src/browser-test-shim.js +++ b/aio/content/examples/http/src/browser-test-shim.js @@ -28,7 +28,6 @@ '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js', '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js', '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', - '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js', '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js', '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js', }, diff --git a/aio/content/examples/i18n/example-config.json b/aio/content/examples/i18n/example-config.json index 99b45f2c49..4b2197f432 100644 --- a/aio/content/examples/i18n/example-config.json +++ b/aio/content/examples/i18n/example-config.json @@ -5,7 +5,8 @@ "cmd": "yarn", "args": [ "e2e", - "--no-webdriver-update" + "--no-webdriver-update", + "--port={PORT}" ] } ] diff --git a/aio/content/examples/interpolation/src/app/app.component.html b/aio/content/examples/interpolation/src/app/app.component.html index 992627d3a5..4a0ed186f2 100644 --- a/aio/content/examples/interpolation/src/app/app.component.html +++ b/aio/content/examples/interpolation/src/app/app.component.html @@ -50,9 +50,10 @@

Template context: template reference variables (#customerInput):

- +
diff --git a/aio/content/examples/interpolation/src/app/app.component.spec.ts b/aio/content/examples/interpolation/src/app/app.component.spec.ts index 7fb8d406db..eb820e6b44 100644 --- a/aio/content/examples/interpolation/src/app/app.component.spec.ts +++ b/aio/content/examples/interpolation/src/app/app.component.spec.ts @@ -14,15 +14,4 @@ describe('AppComponent', () => { const app = fixture.debugElement.componentInstance; expect(app).toBeTruthy(); })); - it(`should have as title 'Featured product:'`, async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.debugElement.componentInstance; - expect(app.title).toEqual('Featured product:'); - })); - it('should render title in a p tag', async(() => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('p').textContent).toContain('Featured product:'); - })); }); diff --git a/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts b/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts index 80649e801f..7942db105d 100644 --- a/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts @@ -1,39 +1,42 @@ 'use strict'; // necessary for es6 output in node -import { browser, element, by } from 'protractor'; +import { browser, element, ElementFinder, by } from 'protractor'; -describe('Lifecycle hooks', function () { +describe('Lifecycle hooks', () => { + const sendKeys = (el: ElementFinder, input: string) => + input.split('').reduce((prev, c) => prev.then(() => el.sendKeys(c)), Promise.resolve()); - beforeAll(function () { + beforeAll(() => { browser.get(''); }); - it('should open correctly', function () { + it('should open correctly', () => { expect(element.all(by.css('h2')).get(0).getText()).toEqual('Peek-A-Boo'); }); - it('should support peek-a-boo', function () { + it('should support peek-a-boo', async () => { let pabComp = element(by.css('peek-a-boo-parent peek-a-boo')); expect(pabComp.isPresent()).toBe(false, 'should not be able to find the "peek-a-boo" component'); + let pabButton = element.all(by.css('peek-a-boo-parent button')).get(0); let updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1); expect(pabButton.getText()).toContain('Create Peek'); - pabButton.click().then(function () { - expect(pabButton.getText()).toContain('Destroy Peek'); - expect(pabComp.isDisplayed()).toBe(true, 'should be able to see the "peek-a-boo" component'); - expect(pabComp.getText()).toContain('Windstorm'); - expect(pabComp.getText()).not.toContain('Windstorm!'); - expect(updateHeroButton.isPresent()).toBe(true, 'should be able to see the update hero button'); - return updateHeroButton.click(); - }).then(function () { - expect(pabComp.getText()).toContain('Windstorm!'); - return pabButton.click(); - }).then(function () { - expect(pabComp.isPresent()).toBe(false, 'should no longer be able to find the "peek-a-boo" component'); - }); + + await pabButton.click(); + expect(pabButton.getText()).toContain('Destroy Peek'); + expect(pabComp.isDisplayed()).toBe(true, 'should be able to see the "peek-a-boo" component'); + expect(pabComp.getText()).toContain('Windstorm'); + expect(pabComp.getText()).not.toContain('Windstorm!'); + expect(updateHeroButton.isPresent()).toBe(true, 'should be able to see the update hero button'); + + await updateHeroButton.click(); + expect(pabComp.getText()).toContain('Windstorm!'); + + await pabButton.click(); + expect(pabComp.isPresent()).toBe(false, 'should no longer be able to find the "peek-a-boo" component'); }); - it('should support OnChanges hook', function () { + it('should support OnChanges hook', () => { let onChangesViewEle = element.all(by.css('on-changes div')).get(0); let inputEles = element.all(by.css('on-changes-parent input')); let heroNameInputEle = inputEles.get(1); @@ -52,7 +55,7 @@ describe('Lifecycle hooks', function () { expect(changeLogEles.count()).toEqual(7, 'should have 7 messages now'); }); - it('should support DoCheck hook', function () { + it('should support DoCheck hook', async () => { let doCheckViewEle = element.all(by.css('do-check div')).get(0); let inputEles = element.all(by.css('do-check-parent input')); let heroNameInputEle = inputEles.get(1); @@ -62,26 +65,25 @@ describe('Lifecycle hooks', function () { let logCount: number; expect(titleEle.getText()).toContain('Windstorm can sing'); - changeLogEles.count().then(function(count: number) { - // 3 messages to start - expect(count).toEqual(3, 'should start with 3 messages'); - logCount = count; - return heroNameInputEle.sendKeys('-foo-'); - }).then(function () { - expect(titleEle.getText()).toContain('Windstorm-foo- can sing'); - return changeLogEles.count(); - }).then(function(count: number) { - // one more for each keystroke - expect(count).toEqual(logCount + 5, 'should add 5 more messages'); - logCount = count; - return powerInputEle.sendKeys('-bar-'); - }).then(function () { - expect(titleEle.getText()).toContain('Windstorm-foo- can sing-bar-'); - expect(changeLogEles.count()).toEqual(logCount + 6, 'should add 6 more messages'); - }); + + let count = await changeLogEles.count(); + // 3 messages to start + expect(count).toEqual(3, 'should start with 3 messages'); + + logCount = count; + await sendKeys(heroNameInputEle, '-foo-'); + count = await changeLogEles.count(); + expect(titleEle.getText()).toContain('Windstorm-foo- can sing'); + expect(count).toBeGreaterThanOrEqual(logCount + 5, 'should add at least one more message for each keystroke'); + + logCount = count; + await sendKeys(powerInputEle, '-bar-'); + count = await changeLogEles.count(); + expect(titleEle.getText()).toContain('Windstorm-foo- can sing-bar-'); + expect(count).toBeGreaterThanOrEqual(logCount + 5, 'should add at least one more message for each keystroke'); }); - it('should support AfterView hooks', function () { + it('should support AfterView hooks', async () => { let parentEle = element(by.tagName('after-view-parent')); let buttonEle = parentEle.element(by.tagName('button')); // Reset let commentEle = parentEle.element(by.className('comment')); @@ -92,90 +94,81 @@ describe('Lifecycle hooks', function () { expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); - logEles.count().then(function(count: number) { - logCount = count; - return childViewInputEle.sendKeys('-test-'); - }).then(function() { - expect(childViewInputEle.getAttribute('value')).toContain('-test-'); - expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); - expect(commentEle.getText()).toContain('long name'); - return logEles.count(); - }).then(function(count: number) { - expect(logCount + 7).toBeGreaterThan(count - 3, - '7 additional log messages should have been added'); - expect(logCount + 7).toBeLessThan(count + 3, - '7 additional log messages should have been added'); - logCount = count; - return buttonEle.click(); - }).then(function() { - expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); - }); + logCount = await logEles.count(); + await childViewInputEle.sendKeys('-test-'); + + expect(childViewInputEle.getAttribute('value')).toContain('-test-'); + expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); + expect(commentEle.getText()).toContain('long name'); + + let count = await logEles.count(); + expect(logCount + 7).toBeGreaterThan(count - 3, '7 additional log messages should have been added'); + expect(logCount + 7).toBeLessThan(count + 3, '7 additional log messages should have been added'); + + logCount = count; + await buttonEle.click(); + expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); }); - - it('should support AfterContent hooks', function () { + it('should support AfterContent hooks', async () => { let parentEle = element(by.tagName('after-content-parent')); let buttonEle = parentEle.element(by.tagName('button')); // Reset let commentEle = parentEle.element(by.className('comment')); let logEles = parentEle.all(by.css('h4 ~ div')); let childViewInputEle = parentEle.element(by.css('app-child input')); - let logCount: number; + let logCount = await logEles.count(); expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); - logEles.count().then(function(count: number) { - logCount = count; - return childViewInputEle.sendKeys('-test-'); - }).then(function() { - expect(childViewInputEle.getAttribute('value')).toContain('-test-'); - expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); - expect(commentEle.getText()).toContain('long name'); - return logEles.count(); - }).then(function(count: number) { - expect(logCount + 5).toEqual(count, '5 additional log messages should have been added'); - logCount = count; - return buttonEle.click(); - }).then(function() { - expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); - }); + await sendKeys(childViewInputEle, '-test-'); + let count = await logEles.count(); + expect(childViewInputEle.getAttribute('value')).toContain('-test-'); + expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); + expect(commentEle.getText()).toContain('long name'); + expect(count).toBeGreaterThanOrEqual(logCount + 5, 'additional log messages should have been added'); + + logCount = count; + await buttonEle.click(); + expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); }); - it('should support spy\'s OnInit & OnDestroy hooks', function () { + it('should support spy\'s OnInit & OnDestroy hooks', async () => { let inputEle = element(by.css('spy-parent input')); let addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero')); let resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes')); let heroEles = element.all(by.css('spy-parent div[mySpy')); let logEles = element.all(by.css('spy-parent h4 ~ div')); + expect(heroEles.count()).toBe(2, 'should have two heroes displayed'); expect(logEles.count()).toBe(2, 'should have two log entries'); - inputEle.sendKeys('-test-').then(function() { - return addHeroButtonEle.click(); - }).then(function() { - expect(heroEles.count()).toBe(3, 'should have added one hero'); - expect(heroEles.get(2).getText()).toContain('-test-'); - expect(logEles.count()).toBe(3, 'should now have 3 log entries'); - return resetHeroesButtonEle.click(); - }).then(function() { - expect(heroEles.count()).toBe(0, 'should no longer have any heroes'); - expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 orig + 1 reset + 3 removeall'); - }); + + await inputEle.sendKeys('-test-'); + await addHeroButtonEle.click(); + expect(heroEles.count()).toBe(3, 'should have added one hero'); + expect(heroEles.get(2).getText()).toContain('-test-'); + expect(logEles.count()).toBe(3, 'should now have 3 log entries'); + + await resetHeroesButtonEle.click(); + expect(heroEles.count()).toBe(0, 'should no longer have any heroes'); + expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 orig + 1 reset + 3 removeall'); }); - it('should support "spy counter"', function () { + it('should support "spy counter"', async () => { let updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update')); let resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset')); let textEle = element(by.css('counter-parent app-counter > div')); let logEles = element.all(by.css('counter-parent h4 ~ div')); + expect(textEle.getText()).toContain('Counter = 0'); expect(logEles.count()).toBe(2, 'should start with two log entries'); - updateCounterButtonEle.click().then(function() { - expect(textEle.getText()).toContain('Counter = 1'); - expect(logEles.count()).toBe(3, 'should now have 3 log entries'); - return resetCounterButtonEle.click(); - }).then(function() { - expect(textEle.getText()).toContain('Counter = 0'); - expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 prev + 1 reset + 2 destroy + 1 init'); - }); + + await updateCounterButtonEle.click(); + expect(textEle.getText()).toContain('Counter = 1'); + expect(logEles.count()).toBe(3, 'should now have 3 log entries'); + + await resetCounterButtonEle.click(); + expect(textEle.getText()).toContain('Counter = 0'); + expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 prev + 1 reset + 2 destroy + 1 init'); }); }); diff --git a/aio/content/examples/ngmodule-faq/contact.1b.stackblitz.json b/aio/content/examples/ngmodule-faq/contact.1b.stackblitz.json deleted file mode 100644 index 2946183c0d..0000000000 --- a/aio/content/examples/ngmodule-faq/contact.1b.stackblitz.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "description": "Contact NgModule v.1", - "files": [ - "src/app/app.component.1b.ts", - "src/app/app.module.1b.ts", - "src/app/highlight.directive.ts", - "src/app/title.component.html", - "src/app/title.component.ts", - "src/app/user.service.ts", - - "src/app/contact/awesome.pipe.ts", - "src/app/contact/contact.component.css", - "src/app/contact/contact.component.html", - "src/app/contact/contact.component.3.ts", - "src/app/contact/contact.service.ts", - "src/app/contact/contact-highlight.directive.ts", - - "src/main.1b.ts", - "src/styles.css", - "src/index.1b.html" - ], - "main": "src/index.1b.html", - "tags": ["NgModule"] -} diff --git a/aio/content/examples/ngmodule-faq/contact.2.stackblitz.json b/aio/content/examples/ngmodule-faq/contact.2.stackblitz.json deleted file mode 100644 index 3aaf1474a6..0000000000 --- a/aio/content/examples/ngmodule-faq/contact.2.stackblitz.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "description": "Contact NgModule v.2", - "files": [ - "src/app/app.component.2.ts", - "src/app/app.module.2.ts", - "src/app/highlight.directive.ts", - "src/app/title.component.html", - "src/app/title.component.ts", - "src/app/user.service.ts", - - "src/app/contact/contact.component.css", - "src/app/contact/contact.component.html", - "src/app/contact/contact.service.ts", - - "src/app/contact/awesome.pipe.ts", - "src/app/contact/contact.component.3.ts", - "src/app/contact/contact.module.2.ts", - "src/app/contact/contact-highlight.directive.ts", - - "src/main.2.ts", - "src/styles.css", - "src/index.2.html" - ], - "main": "src/index.2.html", - "tags": ["NgModule"] -} diff --git a/aio/content/examples/ngmodule-faq/e2e/src/app.e2e-spec.ts b/aio/content/examples/ngmodule-faq/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 72d1ca9214..0000000000 --- a/aio/content/examples/ngmodule-faq/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -1,223 +0,0 @@ -'use strict'; // necessary for es6 output in node - -import { browser, element, by } from 'protractor'; - -describe('NgModule', function () { - - // helpers - const gold = 'rgba(255, 215, 0, 1)'; - const powderblue = 'rgba(176, 224, 230, 1)'; - const lightgray = 'rgba(211, 211, 211, 1)'; - const white = 'rgba(0, 0, 0, 0)'; - - function getCommonsSectionStruct() { - const buttons = element.all(by.css('nav a')); - - return { - title: element.all(by.tagName('h1')).get(0), - welcome: element.all(by.css('app-title p i')).get(0), - contactButton: buttons.get(0), - crisisButton: buttons.get(1), - heroesButton: buttons.get(2) - }; - } - - function getContactSectionStruct() { - const buttons = element.all(by.css('app-contact form button')); - - return { - header: element.all(by.css('app-contact h2')).get(0), - popupMessage: element.all(by.css('app-contact div')).get(0), - contactNameHeader: element.all(by.css('app-contact form h3')).get(0), - input: element.all(by.css('app-contact form input')).get(0), - validationError: element.all(by.css('app-contact form .alert')).get(0), - saveButton: buttons.get(0), // can't be tested - nextContactButton: buttons.get(1), - newContactButton: buttons.get(2) - }; - } - - function getCrisisSectionStruct() { - return { - title: element.all(by.css('ng-component h3')).get(0), - items: element.all(by.css('ng-component a')), - itemId: element.all(by.css('ng-component div')).get(0), - listLink: element.all(by.css('ng-component a')).get(0), - }; - } - - function getHeroesSectionStruct() { - return { - header: element.all(by.css('ng-component h2')).get(0), - title: element.all(by.css('ng-component h3')).get(0), - items: element.all(by.css('ng-component a')), - itemId: element.all(by.css('ng-component ng-component div div')).get(0), - itemInput: element.all(by.css('ng-component ng-component input')).get(0), - listLink: element.all(by.css('ng-component ng-component a')).get(0), - }; - } - - // tests - function appTitleTests(color: string, name?: string) { - return function() { - it('should have a gray header', function() { - const commons = getCommonsSectionStruct(); - expect(commons.title.getCssValue('backgroundColor')).toBe(color); - }); - - it('should welcome us', function () { - const commons = getCommonsSectionStruct(); - expect(commons.welcome.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes')); - }); - }; - } - - function contactTests(color: string, name?: string) { - return function() { - it('shows the contact\'s owner', function() { - const contacts = getContactSectionStruct(); - expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes')); - }); - - it('can cycle between contacts', function () { - const contacts = getContactSectionStruct(); - const nextButton = contacts.nextContactButton; - expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spade'); - expect(contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color); - nextButton.click().then(function () { - expect(contacts.contactNameHeader.getText()).toBe('Awesome Nick Danger'); - return nextButton.click(); - }).then(function () { - expect(contacts.contactNameHeader.getText()).toBe('Awesome Nancy Drew'); - }); - }); - - it('can change an existing contact', function () { - const contacts = getContactSectionStruct(); - contacts.input.sendKeys('a'); - expect(contacts.input.getCssValue('backgroundColor')).toBe(color); - expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spadea'); - }); - - it('can create a new contact', function () { - const contacts = getContactSectionStruct(); - const newContactButton = contacts.newContactButton; - newContactButton.click().then(function () { - expect(contacts.validationError.getText()).toBe('Name is required'); - contacts.input.sendKeys('John Doe'); - expect(contacts.contactNameHeader.getText()).toBe('Awesome John Doe'); - expect(contacts.validationError.getText()).toBe(''); - }); - }); - }; - } - - describe('index.html', function () { - beforeEach(function () { - browser.get(''); - }); - - describe('app-title', appTitleTests(white, 'Miss Marple')); - - describe('contact', contactTests(lightgray, 'Miss Marple')); - - describe('crisis center', function () { - beforeEach(function () { - getCommonsSectionStruct().crisisButton.click(); - }); - - it('shows a list of crisis', function () { - const crisis = getCrisisSectionStruct(); - expect(crisis.title.getText()).toBe('Crisis List'); - expect(crisis.items.count()).toBe(4); - expect(crisis.items.get(0).getText()).toBe('1 - Dragon Burning Cities'); - }); - - it('can navigate to one crisis details', function () { - const crisis = getCrisisSectionStruct(); - crisis.items.get(0).click().then(function() { - expect(crisis.itemId.getText()).toBe('Crisis id: 1'); - return crisis.listLink.click(); - }).then(function () { - // We are back to the list - expect(crisis.items.count()).toBe(4); - }); - }); - }); - - describe('heroes', function () { - beforeEach(function () { - getCommonsSectionStruct().heroesButton.click(); - }); - - it('shows a list of heroes', function() { - const heroes = getHeroesSectionStruct(); - expect(heroes.header.getText()).toBe('Heroes of Miss Marple'); - expect(heroes.title.getText()).toBe('Hero List'); - expect(heroes.items.count()).toBe(6); - expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice'); - }); - - it('can navigate and edit one hero details', function () { - const heroes = getHeroesSectionStruct(); - heroes.items.get(0).click().then(function () { - expect(heroes.itemId.getText()).toBe('Id: 11'); - heroes.itemInput.sendKeys(' try'); - return heroes.listLink.click(); - }).then(function () { - // We are back to the list - expect(heroes.items.count()).toBe(6); - expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice try'); - }); - }); - }); - }); - - // describe('index.0.html', function() { - // beforeEach(function () { - // browser.get('index.0.html'); - // }); - - // it('has a title', function () { - // const title = element.all(by.tagName('h1')).get(0); - // expect(title.getText()).toBe('Minimal NgModule'); - // }); - // }); - - // describe('index.1.html', function () { - // beforeEach(function () { - // browser.get('index.1.html'); - // }); - - // describe('app-title', appTitleTests(powderblue)); - // }); - - // describe('index.1b.html', function () { - // beforeEach(function () { - // browser.get('index.1b.html'); - // }); - - // describe('app-title', appTitleTests(powderblue)); - - // describe('contact', contactTests(powderblue)); - // }); - - // describe('index.2.html', function () { - // beforeEach(function () { - // browser.get('index.2.html'); - // }); - - // describe('app-title', appTitleTests(gold)); - - // describe('contact', contactTests(powderblue)); - // }); - - // describe('index.3.html', function () { - // beforeEach(function () { - // browser.get('index.3.html'); - // }); - - // describe('app-title', appTitleTests(gold)); - // }); - -}); diff --git a/aio/content/examples/ngmodule-faq/minimal.0.stackblitz.json b/aio/content/examples/ngmodule-faq/minimal.0.stackblitz.json deleted file mode 100644 index c1835325d8..0000000000 --- a/aio/content/examples/ngmodule-faq/minimal.0.stackblitz.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "description": "Minimal NgModule", - "files": [ - "src/app/app.component.0.ts", - "src/app/app.module.0.ts", - "src/main.0.ts", - "src/styles.css", - "src/index.0.html" - ], - "main": "src/index.0.html", - "tags": ["NgModule"] -} diff --git a/aio/content/examples/ngmodule-faq/pre-shared.3.stackblitz.json b/aio/content/examples/ngmodule-faq/pre-shared.3.stackblitz.json deleted file mode 100644 index 39faaf229e..0000000000 --- a/aio/content/examples/ngmodule-faq/pre-shared.3.stackblitz.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "description": "NgModule v.3", - "files": [ - "src/app/app.component.3.ts", - "src/app/app.module.3.ts", - "src/app/app-routing.module.3.ts", - - "src/app/highlight.directive.ts", - "src/app/title.component.html", - "src/app/title.component.ts", - "src/app/user.service.ts", - - "src/app/contact/contact.component.css", - "src/app/contact/contact.component.html", - "src/app/contact/contact.service.ts", - - "src/app/contact/awesome.pipe.ts", - "src/app/contact/contact.component.3.ts", - "src/app/contact/contact.module.3.ts", - "src/app/contact/contact-routing.module.3.ts", - "src/app/contact/contact-highlight.directive.ts", - - "src/app/crisis/*.ts", - - "src/app/hero/hero-detail.component.ts", - "src/app/hero/hero-list.component.ts", - "src/app/hero/hero.service.ts", - - "src/app/hero/hero.component.3.ts", - "src/app/hero/hero.module.3.ts", - "src/app/hero/hero-routing.module.3.ts", - "src/app/hero/highlight.directive.ts", - - "src/main.3.ts", - "src/styles.css", - "src/index.3.html" - ], - "main": "src/index.3.html", - "tags": ["NgModule"] -} diff --git a/aio/content/examples/ngmodule-faq/src/app/app-routing.module.3.ts b/aio/content/examples/ngmodule-faq/src/app/app-routing.module.3.ts deleted file mode 100644 index 573e30ed11..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app-routing.module.3.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; - -import { ContactModule } from './contact/contact.module.3'; - -const routes: Routes = [ - { path: '', redirectTo: 'contact', pathMatch: 'full'}, - { path: 'crisis', loadChildren: './crisis/crisis.module#CrisisModule' }, - { path: 'heroes', loadChildren: './hero/hero.module.3#HeroModule' } -]; - -@NgModule({ - imports: [ - ContactModule, - RouterModule.forRoot(routes) - ], - exports: [RouterModule] -}) -export class AppRoutingModule {} diff --git a/aio/content/examples/ngmodule-faq/src/app/app-routing.module.ts b/aio/content/examples/ngmodule-faq/src/app/app-routing.module.ts deleted file mode 100644 index de16e8e6e6..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app-routing.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// #docregion -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; - -import { ContactModule } from './contact/contact.module'; - -// #docregion routes -const routes: Routes = [ - { path: '', redirectTo: 'contact', pathMatch: 'full'}, - // #docregion lazy-routes - { path: 'crisis', loadChildren: './crisis/crisis.module#CrisisModule' }, - { path: 'heroes', loadChildren: './hero/hero.module#HeroModule' } - // #enddocregion lazy-routes -]; -// #enddocregion routes - -@NgModule({ - // #docregion imports - imports: [ - ContactModule, - // #docregion forRoot - RouterModule.forRoot(routes), - // #enddocregion forRoot - ], - // #enddocregion imports - // #docregion exports - exports: [RouterModule] - // #enddocregion exports -}) -export class AppRoutingModule {} diff --git a/aio/content/examples/ngmodule-faq/src/app/app.component.1.ts b/aio/content/examples/ngmodule-faq/src/app/app.component.1.ts deleted file mode 100644 index 6cdf540f0e..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.component.1.ts +++ /dev/null @@ -1,17 +0,0 @@ -// #docplaster -// #docregion -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', -// #enddocregion - /* - // #docregion template - template: '

{{title}}

' - // #enddocregion template - */ -// #docregion - template: '' -}) -export class AppComponent {} -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/app.component.1b.ts b/aio/content/examples/ngmodule-faq/src/app/app.component.1b.ts deleted file mode 100644 index 36e3005e0a..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.component.1b.ts +++ /dev/null @@ -1,13 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - // #docregion template - template: ` - - - ` - // #enddocregion template -}) -export class AppComponent {} diff --git a/aio/content/examples/ngmodule-faq/src/app/app.component.2.ts b/aio/content/examples/ngmodule-faq/src/app/app.component.2.ts deleted file mode 100644 index f28e4526e9..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.component.2.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - template: ` - - - ` -}) -export class AppComponent {} diff --git a/aio/content/examples/ngmodule-faq/src/app/app.component.3.ts b/aio/content/examples/ngmodule-faq/src/app/app.component.3.ts deleted file mode 100644 index cab904e656..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.component.3.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - // #docregion template - template: ` - - - - ` - // #enddocregion template -}) -export class AppComponent {} diff --git a/aio/content/examples/ngmodule-faq/src/app/app.component.ts b/aio/content/examples/ngmodule-faq/src/app/app.component.ts deleted file mode 100644 index 4011f82b0c..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -// #docplaster -// #docregion -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - template: ` - - - - ` -}) -export class AppComponent {} diff --git a/aio/content/examples/ngmodule-faq/src/app/app.module.0.ts b/aio/content/examples/ngmodule-faq/src/app/app.module.0.ts deleted file mode 100644 index 4cce82ffcf..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.module.0.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { AppComponent } from './app.component.0'; - -@NgModule({ -// #docregion imports - imports: [ BrowserModule ], -// #enddocregion imports - declarations: [ AppComponent ], - bootstrap: [ AppComponent ] -}) -export class AppModule { } diff --git a/aio/content/examples/ngmodule-faq/src/app/app.module.1.ts b/aio/content/examples/ngmodule-faq/src/app/app.module.1.ts deleted file mode 100644 index bb0f7ec6f9..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.module.1.ts +++ /dev/null @@ -1,52 +0,0 @@ -// #docplaster -// #docregion -/* Angular Imports */ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -/* App Imports */ -// #enddocregion -import { AppComponent } from './app.component.1'; -/* -// #docregion -import { AppComponent } from './app.component'; -// #enddocregion -*/ -// #docregion -import { HighlightDirective } from './highlight.directive'; -import { TitleComponent } from './title.component'; -import { UserService } from './user.service'; - -/* Contact Related Imports */ -import { FormsModule } from '@angular/forms'; - -import { AwesomePipe } from './contact/awesome.pipe'; -import { ContactComponent } from './contact/contact.component.3'; -import { - ContactHighlightDirective as ContactHighlightDirective -} from './contact/contact-highlight.directive'; - -@NgModule({ -// #docregion imports - imports: [ BrowserModule, FormsModule ], -// #enddocregion imports -// #docregion declarations, directive, component - declarations: [ - AppComponent, - HighlightDirective, -// #enddocregion directive - TitleComponent, -// #enddocregion component - - AwesomePipe, - ContactComponent, - ContactHighlightDirective -// #docregion directive, component - ], -// #enddocregion declarations, directive, component -// #docregion providers - providers: [ UserService ], -// #enddocregion providers - bootstrap: [ AppComponent ] -}) -export class AppModule { } diff --git a/aio/content/examples/ngmodule-faq/src/app/app.module.1b.ts b/aio/content/examples/ngmodule-faq/src/app/app.module.1b.ts deleted file mode 100644 index 9c848aec30..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.module.1b.ts +++ /dev/null @@ -1,46 +0,0 @@ -// #docplaster -// #docregion -/* Angular Imports */ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; - -/* App Imports */ -// #enddocregion -import { AppComponent } from './app.component.1b'; -/* -// #docregion -import { AppComponent } from './app.component'; -// #enddocregion -*/ -// #docregion -import { HighlightDirective } from './highlight.directive'; -import { TitleComponent } from './title.component'; -import { UserService } from './user.service'; - -/* Contact Imports */ -// #enddocregion -import { ContactComponent } from './contact/contact.component.3'; -/* -// #docregion -import { ContactComponent } from './contact/contact.component'; -// #enddocregion -*/ -// #docregion -import { AwesomePipe } from './contact/awesome.pipe'; -import { ContactService } from './contact/contact.service'; -import { ContactHighlightDirective } from './contact/contact-highlight.directive'; - -@NgModule({ - imports: [ BrowserModule, FormsModule ], -// #docregion declarations - declarations: [ - AppComponent, HighlightDirective, TitleComponent, - AwesomePipe, ContactComponent, ContactHighlightDirective - ], -// #docregion providers - providers: [ ContactService, UserService ], -// #enddocregion providers - bootstrap: [ AppComponent ] -}) -export class AppModule { } diff --git a/aio/content/examples/ngmodule-faq/src/app/app.module.2.ts b/aio/content/examples/ngmodule-faq/src/app/app.module.2.ts deleted file mode 100644 index 0da0fe334f..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.module.2.ts +++ /dev/null @@ -1,36 +0,0 @@ -// #docplaster -// #docregion -/* Angular Imports */ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -/* App Imports */ -// #enddocregion -import { AppComponent } from './app.component.2'; -/* -// #docregion -import { AppComponent } from './app.component'; -// #enddocregion -*/ -// #docregion -import { HighlightDirective } from './highlight.directive'; -import { TitleComponent } from './title.component'; -import { UserService } from './user.service'; - -/* Contact Imports */ -// #enddocregion -import { ContactModule } from './contact/contact.module.2'; -/* -// #docregion -import { ContactModule } from './contact/contact.module'; -// #enddocregion -*/ -// #docregion - -@NgModule({ - imports: [ BrowserModule, ContactModule ], - declarations: [ AppComponent, HighlightDirective, TitleComponent ], - providers: [ UserService ], - bootstrap: [ AppComponent ], -}) -export class AppModule { } diff --git a/aio/content/examples/ngmodule-faq/src/app/app.module.ts b/aio/content/examples/ngmodule-faq/src/app/app.module.ts deleted file mode 100644 index c75c445a58..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/app.module.ts +++ /dev/null @@ -1,39 +0,0 @@ -// #docplaster -// #docregion -// #docregion v4 -/* Angular Imports */ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -/* App Imports */ -import { AppComponent } from './app.component'; - -/* Core Modules */ -import { CoreModule } from './core/core.module'; - -/* Routing Module */ -import { AppRoutingModule } from './app-routing.module'; - -@NgModule({ - // #docregion import-for-root - imports: [ - BrowserModule, -// #enddocregion v4 -// #enddocregion import-for-root -/* -// #docregion v4 - CoreModule, -// #enddocregion v4 -*/ -// #docregion import-for-root - CoreModule.forRoot({userName: 'Miss Marple'}), -// #docregion v4 - AppRoutingModule - ], - // #enddocregion import-for-root - declarations: [ AppComponent ], - bootstrap: [ AppComponent ] -}) -export class AppModule { } -// #enddocregion v4 -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact-routing.module.3.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact-routing.module.3.ts deleted file mode 100644 index d89119a156..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact-routing.module.3.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { ContactComponent } from './contact.component.3'; - -const routes = [ - { path: 'contact', component: ContactComponent} -]; - -@NgModule({ - imports: [ RouterModule.forChild(routes) ], - exports: [ RouterModule ] -}) -export class ContactRoutingModule {} diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact-routing.module.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact-routing.module.ts deleted file mode 100644 index 46dd15867e..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact-routing.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { ContactComponent } from './contact.component'; - -// #docregion routing -const routes = [ - { path: 'contact', component: ContactComponent} -]; - -@NgModule({ - imports: [ RouterModule.forChild(routes) ], - exports: [ RouterModule ] -}) -export class ContactRoutingModule {} -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.3.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.3.ts deleted file mode 100644 index e674ec1323..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.3.ts +++ /dev/null @@ -1,53 +0,0 @@ -// #docregion -import { Component, OnInit } from '@angular/core'; - -import { Contact, ContactService } from './contact.service'; -import { UserService } from '../user.service'; - -@Component({ - selector: 'app-contact', - templateUrl: './contact.component.html', - styleUrls: [ './contact.component.css' ] -}) -export class ContactComponent implements OnInit { - contact: Contact; - contacts: Contact[]; - - msg = 'Loading contacts ...'; - userName = ''; - - constructor(private contactService: ContactService, userService: UserService) { - this.userName = userService.userName; - } - - ngOnInit() { - this.contactService.getContacts().subscribe(contacts => { - this.msg = ''; - this.contacts = contacts; - this.contact = contacts[0]; - }); - } - - next() { - let ix = 1 + this.contacts.indexOf(this.contact); - if (ix >= this.contacts.length) { ix = 0; } - this.contact = this.contacts[ix]; - } - - onSubmit() { - // POST-DEMO TODO: do something like save it - this.displayMessage('Saved ' + this.contact.name); - } - - newContact() { - this.displayMessage('New contact'); - this.contact = {id: 42, name: ''}; - this.contacts.push(this.contact); - } - - /** Display a message briefly, then remove it. */ - displayMessage(msg: string) { - this.msg = msg; - setTimeout(() => this.msg = '', 1500); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.css b/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.css deleted file mode 100644 index d5bd954dde..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.css +++ /dev/null @@ -1,32 +0,0 @@ -/* #docregion */ -.ng-valid[required] { - border-left: 5px solid #42A948; /* green */ -} - -.ng-invalid { - border-left: 5px solid #a94442; /* red */ -} - -.alert { - padding: 15px; - margin: 8px 0; - border: 1px solid transparent; - border-radius: 4px; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} - -.msg { - color: blue; - background-color: whitesmoke; - border: 1px solid transparent; - border-radius: 4px; - margin-bottom: 20px; -} - -.button-group { - padding-top: 12px; -} diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.html b/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.html deleted file mode 100644 index ecd5e62169..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.html +++ /dev/null @@ -1,37 +0,0 @@ - -

Contact of {{userName}}

-
{{msg}}
- -
- -

{{ contact.name | awesome }}

- - -
- - - - - - -
- Name is required -
-
- -
- - - - - -
-
- diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.ts deleted file mode 100644 index 7fa44eebe5..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.component.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Exact copy except import UserService from core -// #docregion -import { Component, OnInit } from '@angular/core'; - -import { Contact, ContactService } from './contact.service'; -import { UserService } from '../core/user.service'; - -@Component({ - selector: 'app-contact', - templateUrl: './contact.component.html', - styleUrls: [ './contact.component.css' ] -}) -export class ContactComponent implements OnInit { - contact: Contact; - contacts: Contact[]; - - msg = 'Loading contacts ...'; - userName = ''; - - constructor(private contactService: ContactService, userService: UserService) { - this.userName = userService.userName; - } - - ngOnInit() { - this.contactService.getContacts().subscribe(contacts => { - this.msg = ''; - this.contacts = contacts; - this.contact = contacts[0]; - }); - } - - next() { - let ix = 1 + this.contacts.indexOf(this.contact); - if (ix >= this.contacts.length) { ix = 0; } - this.contact = this.contacts[ix]; - } - - onSubmit() { - // POST-DEMO TODO: do something like save it - this.displayMessage('Saved ' + this.contact.name); - } - - newContact() { - this.displayMessage('New contact'); - this.contact = {id: 42, name: ''}; - this.contacts.push(this.contact); - } - - /** Display a message briefly, then remove it. */ - displayMessage(msg: string) { - this.msg = msg; - setTimeout(() => this.msg = '', 1500); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.0.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.0.ts deleted file mode 100644 index fe2a756bd4..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.0.ts +++ /dev/null @@ -1,11 +0,0 @@ -// #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -@NgModule({ - imports: [ - CommonModule - ], - declarations: [] -}) -export class ContactModule { } diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.2.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.2.ts deleted file mode 100644 index d83a26cd78..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.2.ts +++ /dev/null @@ -1,37 +0,0 @@ -// #docplaster -// #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; - -import { AwesomePipe } from './awesome.pipe'; -// #enddocregion -import { ContactComponent } from './contact.component.3'; -/* -// #docregion -import { ContactComponent } from './contact.component'; -// #enddocregion -*/ -// #docregion -import { ContactHighlightDirective } from './contact-highlight.directive'; -import { ContactService } from './contact.service'; - -// #docregion class -@NgModule({ - imports: [ - CommonModule, - FormsModule - ], - declarations: [ - AwesomePipe, - ContactComponent, - ContactHighlightDirective - ], - // #docregion exports - exports: [ ContactComponent ], - // #enddocregion exports - providers: [ ContactService ] -}) -export class ContactModule { } -// #enddocregion class -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.3.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.3.ts deleted file mode 100644 index 3af9732e76..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.3.ts +++ /dev/null @@ -1,44 +0,0 @@ -// #docplaster -// #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; - -import { AwesomePipe } from './awesome.pipe'; -// #enddocregion -import { ContactComponent } from './contact.component.3'; -/* -// #docregion -import { ContactComponent } from './contact.component'; -// #enddocregion -*/ -// #docregion -import { ContactHighlightDirective } from './contact-highlight.directive'; -import { ContactService } from './contact.service'; - -// #enddocregion -import { ContactRoutingModule } from './contact-routing.module.3'; -/* -// #docregion -import { ContactRoutingModule } from './contact-routing.module'; -// #enddocregion -*/ -// #docregion - -// #docregion class -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ContactRoutingModule - ], - declarations: [ - AwesomePipe, - ContactComponent, - ContactHighlightDirective - ], - providers: [ ContactService ] -}) -export class ContactModule { } -// #enddocregion class -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.ts deleted file mode 100644 index 883331dfba..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -// #docregion -import { NgModule } from '@angular/core'; -import { SharedModule } from '../shared/shared.module'; - -import { ContactComponent } from './contact.component'; -import { ContactService } from './contact.service'; -import { ContactRoutingModule } from './contact-routing.module'; - -// #docregion class -@NgModule({ - imports: [ - SharedModule, - ContactRoutingModule - ], - declarations: [ ContactComponent ], - providers: [ ContactService ] -}) -export class ContactModule { } -// #enddocregion class diff --git a/aio/content/examples/ngmodule-faq/src/app/contact/contact.service.ts b/aio/content/examples/ngmodule-faq/src/app/contact/contact.service.ts deleted file mode 100644 index ff21a65bce..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/contact/contact.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -// #docplaster -// #docregion -import { Injectable, OnDestroy } from '@angular/core'; - -import { Observable, of } from 'rxjs'; -import { delay } from 'rxjs/operators'; - -export class Contact { - constructor(public id: number, public name: string) { } -} - -const CONTACTS: Contact[] = [ - new Contact(21, 'Sam Spade'), - new Contact(22, 'Nick Danger'), - new Contact(23, 'Nancy Drew') -]; - -const FETCH_LATENCY = 500; - -/** Simulate a data service that retrieves contacts from a server */ -@Injectable() -export class ContactService implements OnDestroy { -// #enddocregion - constructor() { console.log('ContactService instance created.'); } - ngOnDestroy() { console.log('ContactService instance destroyed.'); } - -// #docregion - getContacts(): Observable { - return of(CONTACTS).pipe(delay(FETCH_LATENCY)); - } - - getContact(id: number | string): Observable { - return of(CONTACTS.find(contact => contact.id === +id)) - .pipe(delay(FETCH_LATENCY)); - } -} -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/core/core.module.ts b/aio/content/examples/ngmodule-faq/src/app/core/core.module.ts deleted file mode 100644 index 27cb9a2193..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/core/core.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* tslint:disable:member-ordering no-unused-variable */ -// #docplaster -// #docregion -// #docregion v4 -import { - ModuleWithProviders, NgModule, - Optional, SkipSelf } from '@angular/core'; - -import { CommonModule } from '@angular/common'; - -import { TitleComponent } from './title.component'; -import { UserService } from './user.service'; -// #enddocregion -import { UserServiceConfig } from './user.service'; - -// #docregion v4 -@NgModule({ - imports: [ CommonModule ], - declarations: [ TitleComponent ], - exports: [ TitleComponent ], - providers: [ UserService ] -}) -export class CoreModule { -// #enddocregion v4 - - // #docregion ctor - constructor (@Optional() @SkipSelf() parentModule: CoreModule) { - if (parentModule) { - throw new Error( - 'CoreModule is already loaded. Import it in the AppModule only'); - } - } - // #enddocregion ctor - - // #docregion for-root - static forRoot(config: UserServiceConfig): ModuleWithProviders { - return { - ngModule: CoreModule, - providers: [ - {provide: UserServiceConfig, useValue: config } - ] - }; - } - // #enddocregion for-root -// #docregion v4 -} -// #enddocregion v4 -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/core/title.component.html b/aio/content/examples/ngmodule-faq/src/app/core/title.component.html deleted file mode 100644 index be114caa73..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/core/title.component.html +++ /dev/null @@ -1,6 +0,0 @@ - -

{{title}}

-

- Welcome, {{user}} -

- diff --git a/aio/content/examples/ngmodule-faq/src/app/core/title.component.ts b/aio/content/examples/ngmodule-faq/src/app/core/title.component.ts deleted file mode 100644 index 51d9c242db..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/core/title.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Exact copy of app/title.component.ts except import UserService from shared -import { Component, Input } from '@angular/core'; -import { UserService } from '../core/user.service'; - -@Component({ - selector: 'app-title', - templateUrl: './title.component.html', -}) -export class TitleComponent { - title = 'Angular Modules'; - user = ''; - - constructor(userService: UserService) { - this.user = userService.userName; - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/core/user.service.ts b/aio/content/examples/ngmodule-faq/src/app/core/user.service.ts deleted file mode 100644 index 8fe839075e..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/core/user.service.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Crazy copy of the app/user.service -// Proves that UserService is an app-wide singleton and only instantiated once -// IFF shared.module follows the `forRoot` pattern -// -// If it didn't, a new instance of UserService would be created -// after each lazy load and the userName would double up. - -import { Injectable, Optional } from '@angular/core'; - -let nextId = 1; - -export class UserServiceConfig { - userName = 'Philip Marlowe'; -} - -@Injectable() -export class UserService { - id = nextId++; - private _userName = 'Sherlock Holmes'; - - // #docregion ctor - constructor(@Optional() config: UserServiceConfig) { - if (config) { this._userName = config.userName; } - } - // #enddocregion ctor - - get userName() { - // Demo: add a suffix if this service has been created more than once - const suffix = this.id > 1 ? ` times ${this.id}` : ''; - return this._userName + suffix; - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-detail.component.ts b/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-detail.component.ts deleted file mode 100644 index 30a1d0e7bf..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-detail.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -@Component({ - template: ` -

Crisis Detail

-
Crisis id: {{id}}
-
- Crisis List - ` -}) -export class CrisisDetailComponent implements OnInit { - id: number; - constructor(private route: ActivatedRoute) { } - - ngOnInit() { - this.id = parseInt(this.route.snapshot.paramMap.get('id'), 10); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-list.component.ts b/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-list.component.ts deleted file mode 100644 index 858f749416..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-list.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component } from '@angular/core'; -import { Observable } from 'rxjs'; - -import { Crisis, - CrisisService } from './crisis.service'; - -@Component({ - template: ` -

Crisis List

- - ` -}) -export class CrisisListComponent { - crises: Observable; - - constructor(private crisisService: CrisisService) { - this.crises = this.crisisService.getCrises(); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-routing.module.ts b/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-routing.module.ts deleted file mode 100644 index c60efa8cb4..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, - RouterModule } from '@angular/router'; - -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; - -const routes: Routes = [ - { path: '', redirectTo: 'list', pathMatch: 'full'}, - { path: 'list', component: CrisisListComponent }, - { path: ':id', component: CrisisDetailComponent } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class CrisisRoutingModule {} diff --git a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis.module.ts b/aio/content/examples/ngmodule-faq/src/app/crisis/crisis.module.ts deleted file mode 100644 index f557bd6423..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; -import { CrisisService } from './crisis.service'; -import { CrisisRoutingModule } from './crisis-routing.module'; - -@NgModule({ - imports: [ CommonModule, CrisisRoutingModule ], - declarations: [ CrisisDetailComponent, CrisisListComponent ], - providers: [ CrisisService ] -}) -export class CrisisModule {} diff --git a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis.service.ts b/aio/content/examples/ngmodule-faq/src/app/crisis/crisis.service.ts deleted file mode 100644 index f91eb995ea..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/crisis/crisis.service.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable, OnDestroy } from '@angular/core'; - -import { Observable, of } from 'rxjs'; -import { delay } from 'rxjs/operators'; - -export class Crisis { - constructor(public id: number, public name: string) { } -} - -const CRISES: Crisis[] = [ - new Crisis(1, 'Dragon Burning Cities'), - new Crisis(2, 'Sky Rains Great White Sharks'), - new Crisis(3, 'Giant Asteroid Heading For Earth'), - new Crisis(4, 'Procrastinators Meeting Delayed Again'), -]; - -const FETCH_LATENCY = 500; - -/** Simulate a data service that retrieves crises from a server */ -@Injectable() -export class CrisisService implements OnDestroy { - constructor() { console.log('CrisisService instance created.'); } - ngOnDestroy() { console.log('CrisisService instance destroyed.'); } - - getCrises(): Observable { - return of(CRISES).pipe(delay(FETCH_LATENCY)); - } - - getCrisis(id: number | string): Observable { - return of(CRISES.find(crisis => crisis.id === +id)) - .pipe(delay(FETCH_LATENCY)); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero-detail.component.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero-detail.component.ts deleted file mode 100644 index 2c46622bc4..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero-detail.component.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { Hero, - HeroService } from './hero.service'; - -@Component({ - template: ` -

Hero Detail

-
-
Id: {{hero.id}}

- -
-
- Hero List - ` -}) -export class HeroDetailComponent implements OnInit { - hero: Hero; - - constructor( - private route: ActivatedRoute, - private heroService: HeroService) { } - - ngOnInit() { - let id = parseInt(this.route.snapshot.paramMap.get('id'), 10); - this.heroService.getHero(id).subscribe(hero => this.hero = hero); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero-list.component.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero-list.component.ts deleted file mode 100644 index 8ac492403e..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero-list.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component } from '@angular/core'; -import { Observable } from 'rxjs'; - -import { Hero, - HeroService } from './hero.service'; - -@Component({ - template: ` -

Hero List

- - ` -}) -export class HeroListComponent { - heroes: Observable; - constructor(private heroService: HeroService) { - this.heroes = this.heroService.getHeroes(); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero-routing.module.3.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero-routing.module.3.ts deleted file mode 100644 index 588ffd94be..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero-routing.module.3.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, - RouterModule } from '@angular/router'; - -import { HeroComponent } from './hero.component.3'; -import { HeroListComponent } from './hero-list.component'; -import { HeroDetailComponent } from './hero-detail.component'; - -const routes: Routes = [ - { path: '', - component: HeroComponent, - children: [ - { path: '', component: HeroListComponent }, - { path: ':id', component: HeroDetailComponent } - ] - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class HeroRoutingModule {} diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero-routing.module.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero-routing.module.ts deleted file mode 100644 index d97aab3beb..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero-routing.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, - RouterModule } from '@angular/router'; - -import { HeroComponent } from './hero.component'; -import { HeroListComponent } from './hero-list.component'; -import { HeroDetailComponent } from './hero-detail.component'; - -const routes: Routes = [ - { path: '', - component: HeroComponent, - children: [ - { path: '', component: HeroListComponent }, - { path: ':id', component: HeroDetailComponent } - ] - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class HeroRoutingModule {} diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero.component.3.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero.component.3.ts deleted file mode 100644 index d52bc253df..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero.component.3.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component } from '@angular/core'; - -import { HeroService } from './hero.service'; -import { UserService } from '../user.service'; - -@Component({ - template: ` -

Heroes of {{userName}}

- - `, - providers: [ HeroService ] -}) -export class HeroComponent { - userName = ''; - constructor(userService: UserService) { - this.userName = userService.userName; - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero.component.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero.component.ts deleted file mode 100644 index 86338fb0ae..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Exact copy except import UserService from core -import { Component } from '@angular/core'; - -import { HeroService } from './hero.service'; -import { UserService } from '../core/user.service'; - -@Component({ - template: ` -

Heroes of {{userName}}

- - `, - providers: [ HeroService ] -}) -export class HeroComponent { - userName = ''; - constructor(userService: UserService) { - this.userName = userService.userName; - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero.module.3.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero.module.3.ts deleted file mode 100644 index acd5e28b15..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero.module.3.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; - -import { HeroComponent } from './hero.component.3'; -import { HeroDetailComponent } from './hero-detail.component'; -import { HeroListComponent } from './hero-list.component'; -import { HeroRoutingModule } from './hero-routing.module.3'; - -import { HighlightDirective } from './highlight.directive'; - -// #docregion class -@NgModule({ - imports: [ CommonModule, FormsModule, HeroRoutingModule ], - declarations: [ - HeroComponent, HeroDetailComponent, HeroListComponent, - HighlightDirective - ] -}) -export class HeroModule { } -// #enddocregion class diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero.module.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero.module.ts deleted file mode 100644 index 98d7b76b00..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { SharedModule } from '../shared/shared.module'; - -import { HeroComponent } from './hero.component'; -import { HeroDetailComponent } from './hero-detail.component'; -import { HeroListComponent } from './hero-list.component'; -import { HeroRoutingModule } from './hero-routing.module'; - -@NgModule({ - imports: [ SharedModule, HeroRoutingModule ], - declarations: [ - HeroComponent, HeroDetailComponent, HeroListComponent, - ] -}) -export class HeroModule { } diff --git a/aio/content/examples/ngmodule-faq/src/app/hero/hero.service.ts b/aio/content/examples/ngmodule-faq/src/app/hero/hero.service.ts deleted file mode 100644 index da3677845f..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/hero/hero.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Injectable, OnDestroy } from '@angular/core'; - -import { Observable, of } from 'rxjs'; -import { delay } from 'rxjs/operators'; - -export class Hero { - constructor(public id: number, public name: string) { } -} - -const HEROES: Hero[] = [ - new Hero(11, 'Mr. Nice'), - new Hero(12, 'Narco'), - new Hero(13, 'Bombasto'), - new Hero(14, 'Celeritas'), - new Hero(15, 'Magneta'), - new Hero(16, 'RubberMan') -]; - -const FETCH_LATENCY = 500; - -/** Simulate a data service that retrieves heroes from a server */ -@Injectable() -export class HeroService implements OnDestroy { - - constructor() { console.log('HeroService instance created.'); } - ngOnDestroy() { console.log('HeroService instance destroyed.'); } - - getHeroes(): Observable { - return of(HEROES).pipe(delay(FETCH_LATENCY)); - } - - getHero(id: number | string): Observable { - return of(HEROES.find(hero => hero.id === +id)) - .pipe(delay(FETCH_LATENCY)); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/shared/awesome.pipe.ts b/aio/content/examples/ngmodule-faq/src/app/shared/awesome.pipe.ts deleted file mode 100644 index a1a0001d24..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/shared/awesome.pipe.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Exact copy of contact.awesome.pipe -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ name: 'awesome' }) -/** Precede the input string with the word "Awesome " */ -export class AwesomePipe implements PipeTransform { - transform(phrase: string) { - return phrase ? 'Awesome ' + phrase : ''; - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/shared/highlight.directive.ts b/aio/content/examples/ngmodule-faq/src/app/shared/highlight.directive.ts deleted file mode 100644 index ee874fa536..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/shared/highlight.directive.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Exact copy of contact/highlight.directive except for color and message -import { Directive, ElementRef } from '@angular/core'; - -@Directive({ selector: '[highlight], input' }) -// Highlight the host element or any InputElement in gray -export class HighlightDirective { - constructor(el: ElementRef) { - el.nativeElement.style.backgroundColor = 'lightgray'; - console.log( - `* Shared highlight called for ${el.nativeElement.tagName}`); - } -} diff --git a/aio/content/examples/ngmodule-faq/src/app/shared/shared.module.ts b/aio/content/examples/ngmodule-faq/src/app/shared/shared.module.ts deleted file mode 100644 index 2da7d7b2a5..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/shared/shared.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -// #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; - -import { AwesomePipe } from './awesome.pipe'; -import { HighlightDirective } from './highlight.directive'; - -// #docregion module -@NgModule({ - imports: [ CommonModule ], - declarations: [ AwesomePipe, HighlightDirective ], - exports: [ AwesomePipe, HighlightDirective, - CommonModule, FormsModule ] -}) -export class SharedModule { } -// #enddocregion module -// #enddocregion diff --git a/aio/content/examples/ngmodule-faq/src/app/user.service.ts b/aio/content/examples/ngmodule-faq/src/app/user.service.ts deleted file mode 100644 index 7d996b26fa..0000000000 --- a/aio/content/examples/ngmodule-faq/src/app/user.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -// #docregion -import { Injectable } from '@angular/core'; - -@Injectable() -/** Dummy version of an authenticated user service */ -export class UserService { - userName = 'Sherlock Holmes'; -} diff --git a/aio/content/examples/ngmodule-faq/src/index.1.html b/aio/content/examples/ngmodule-faq/src/index.1.html deleted file mode 100644 index 2da2e67b4b..0000000000 --- a/aio/content/examples/ngmodule-faq/src/index.1.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - NgModule Minimal - - - - - - - - diff --git a/aio/content/examples/ngmodule-faq/src/index.1b.html b/aio/content/examples/ngmodule-faq/src/index.1b.html deleted file mode 100644 index 2da2e67b4b..0000000000 --- a/aio/content/examples/ngmodule-faq/src/index.1b.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - NgModule Minimal - - - - - - - - diff --git a/aio/content/examples/ngmodule-faq/src/index.2.html b/aio/content/examples/ngmodule-faq/src/index.2.html deleted file mode 100644 index 2da2e67b4b..0000000000 --- a/aio/content/examples/ngmodule-faq/src/index.2.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - NgModule Minimal - - - - - - - - diff --git a/aio/content/examples/ngmodule-faq/src/index.3.html b/aio/content/examples/ngmodule-faq/src/index.3.html deleted file mode 100644 index 2da2e67b4b..0000000000 --- a/aio/content/examples/ngmodule-faq/src/index.3.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - NgModule Minimal - - - - - - - - diff --git a/aio/content/examples/ngmodule-faq/src/index.html b/aio/content/examples/ngmodule-faq/src/index.html deleted file mode 100644 index cb62943973..0000000000 --- a/aio/content/examples/ngmodule-faq/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - NgModule Deluxe - - - - - - - - diff --git a/aio/content/examples/ngmodule-faq/src/main-static.ts b/aio/content/examples/ngmodule-faq/src/main-static.ts deleted file mode 100644 index a1bcf466d2..0000000000 --- a/aio/content/examples/ngmodule-faq/src/main-static.ts +++ /dev/null @@ -1,13 +0,0 @@ -// #docplaster -/* -// #docregion -// The browser platform without a compiler -import { platformBrowser } from '@angular/platform-browser'; - -// The app module factory produced by the static offline compiler -import { AppModuleNgFactory } from './app/app.module.ngfactory'; - -// Launch with the app module factory. -platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); -// #enddocregion -*/ diff --git a/aio/content/examples/ngmodule-faq/src/main.1b.ts b/aio/content/examples/ngmodule-faq/src/main.1b.ts deleted file mode 100644 index a0095ac64d..0000000000 --- a/aio/content/examples/ngmodule-faq/src/main.1b.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module.1b'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/aio/content/examples/ngmodule-faq/src/main.3.ts b/aio/content/examples/ngmodule-faq/src/main.3.ts deleted file mode 100644 index 633b21147f..0000000000 --- a/aio/content/examples/ngmodule-faq/src/main.3.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module.3'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/aio/content/examples/ngmodule-faq/stackblitz.json b/aio/content/examples/ngmodule-faq/stackblitz.json deleted file mode 100644 index b56eaeebf8..0000000000 --- a/aio/content/examples/ngmodule-faq/stackblitz.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "description": "NgModule Final", - "files": [ - "src/app/app.component.ts", - "src/app/app.module.ts", - "src/app/app-routing.module.ts", - - "src/app/contact/contact.component.css", - "src/app/contact/contact.component.html", - "src/app/contact/contact.service.ts", - - "src/app/contact/contact.component.ts", - "src/app/contact/contact.module.ts", - "src/app/contact/contact-routing.module.ts", - - "src/app/crisis/*.ts", - - "src/app/hero/hero-detail.component.ts", - "src/app/hero/hero-list.component.ts", - "src/app/hero/hero.service.ts", - - "src/app/hero/hero.component.ts", - "src/app/hero/hero.module.ts", - "src/app/hero/hero-routing.module.ts", - - "src/app/core/*.css", - "src/app/core/*.html", - "src/app/core/*.ts", - - "src/app/shared/*.css", - "src/app/shared/*.html", - "src/app/shared/*.ts", - - "src/main.ts", - "src/styles.css", - "src/index.html" - ], - "main": "src/index.html", - "tags": ["NgModule"] -} diff --git a/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts b/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts index 42207ea22c..c1d63cf14c 100644 --- a/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts @@ -5,8 +5,6 @@ import { browser, element, by } from 'protractor'; describe('NgModule-example', function () { // helpers - const gold = 'rgba(255, 215, 0, 1)'; - const powderblue = 'rgba(176, 224, 230, 1)'; const lightgray = 'rgba(239, 238, 237, 1)'; const white = 'rgba(0, 0, 0, 0)'; @@ -15,7 +13,7 @@ describe('NgModule-example', function () { return { title: element.all(by.tagName('h1')).get(0), - subtitle: element.all(by.css('app-title p i')).get(0), + subtitle: element.all(by.css('app-root p i')).get(0), contactButton: buttons.get(0), itemButton: buttons.get(1), customersButton: buttons.get(2) @@ -67,7 +65,7 @@ describe('NgModule-example', function () { it('should welcome us', function () { const commons = getCommonsSectionStruct(); - expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes')); + expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Miss Marple')); }); }; } @@ -76,7 +74,7 @@ describe('NgModule-example', function () { return function() { it('shows the contact\'s owner', function() { const contacts = getContactSectionStruct(); - expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes')); + expect(contacts.header.getText()).toBe((name || 'Miss Marple') + '\'s Contacts'); }); it('can cycle between contacts', function () { @@ -92,21 +90,22 @@ describe('NgModule-example', function () { }); }); - it('can change an existing contact', function () { - const contacts = getContactSectionStruct(); - contacts.input.sendKeys('a'); - expect(contacts.input.getCssValue('backgroundColor')).toBe(color); - expect(contacts.contactNameHeader.getText()).toBe('Awesome Yashaa'); - }); - it('can create a new contact', function () { const contacts = getContactSectionStruct(); const newContactButton = contacts.newContactButton; + const nextButton = contacts.nextContactButton; + const input = contacts.input; + const saveButton = contacts.saveButton; + newContactButton.click().then(function () { - expect(contacts.validationError.getText()).toBe('Name is required'); - contacts.input.sendKeys('John Doe'); - expect(contacts.contactNameHeader.getText()).toBe('Awesome John Doe'); - expect(contacts.validationError.getText()).toBe(''); + input.click(); + nextButton.click() + expect(contacts.validationError.getText()).toBe('Name is required.'); + input.click(); + contacts.input.sendKeys('Watson'); + saveButton.click() + expect(contacts.contactNameHeader.getText()).toBe('Awesome Watson'); + }); }); }; diff --git a/aio/content/examples/ngmodules/src/app/app.component.ts b/aio/content/examples/ngmodules/src/app/app.component.ts index 41c28b9ed6..0fda5ec49b 100644 --- a/aio/content/examples/ngmodules/src/app/app.component.ts +++ b/aio/content/examples/ngmodules/src/app/app.component.ts @@ -3,7 +3,7 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` - +