build: add npm_integration_test && angular_integration_test (#33927)

* it's tricky to get out of the runfiles tree with `bazel test` as `BUILD_WORKSPACE_DIRECTORY` is not set but I employed a trick to read the `DO_NOT_BUILD_HERE` file that is one level up from `execroot` and that contains the workspace directory. This is experimental and if `bazel test //:test.debug` fails than `bazel run` is still guaranteed to work as  `BUILD_WORKSPACE_DIRECTORY` will be set in that context

* test //integration:bazel_test and //integration:bazel-schematics_test exclusively

* run "exclusive" and "manual" bazel-in-bazel integration tests in their own CI job as they take 8m+ to execute

```
//integration:bazel-schematics_test                                      PASSED in 317.2s
//integration:bazel_test                                                 PASSED in 167.8s
```

* Skip all integration tests that are now handled by angular_integration_test except the tests that are tracked for payload size; these are:
- cli-hello-world*
- hello_world__closure

* add & pin @babel deps as newer versions of babel break //packages/localize/src/tools/test:test

@babel/core dep had to be pinned to 7.6.4 or else //packages/localize/src/tools/test:test failed. Also //packages/localize uses @babel/generator, @babel/template, @babel/traverse & @babel/types so these deps were added to package.json as they were not being hoisted anymore from @babel/core transitive.

NB: integration/hello_world__systemjs_umd test must run with systemjs 0.20.0
NB: systemjs must be at 0.18.10 for legacy saucelabs job to pass
NB: With Bazel 2.0, the glob for the files to test `"integration/bazel/**"` is empty if integation/bazel is in .bazelignore. This glob worked under these conditions with 1.1.0. I did not bother testing with 1.2.x as not having integration/bazel in .bazelignore is correct.

PR Close #33927
This commit is contained in:
Greg Magolan 2020-02-04 11:45:40 -08:00 committed by Miško Hevery
parent e084835fb1
commit dde68ff954
79 changed files with 7011 additions and 3750 deletions

View File

@ -4,7 +4,57 @@ dist
aio/content
aio/node_modules
aio/tools/examples/shared/node_modules
integration/bazel
integration/bazel/bazel-bazel
integration/bazel/bazel-bin
integration/bazel/bazel-out
integration/bazel/bazel-testlogs
integration/bazel/node_modules
integration/bazel/.yarn_local_cache
integration/bazel-schematics/node_modules
integration/bazel-schematics/.yarn_local_cache
integration/bazel-schematics/demo
integration/cli-hello-world/node_modules
integration/cli-hello-world-ivy-compat/node_modules
integration/cli-hello-world-ivy-i18n/node_modules
integration/cli-hello-world-ivy-minimal/node_modules
integration/cli-hello-world-lazy/node_modules
integration/cli-hello-world-lazy-rollup/node_modules
integration/dynamic-compiler/node_modules
integration/hello_world__closure/node_modules
integration/hello_world__systemjs_umd/node_modules
integration/i18n/node_modules
integration/injectable-def/node_modules
integration/ivy-i18n/node_modules
integration/language_service_plugin/node_modules
integration/ng_elements/node_modules
integration/ng_update/node_modules
integration/ng_update_migrations/node_modules
integration/ngcc/node_modules
integration/platform-server/node_modules
integration/service-worker-schema/node_modules
integration/side-effects/node_modules
integration/terser/node_modules
integration/typings_test_ts36/node_modules
integration/cli-hello-world/.yarn_local_cache
integration/cli-hello-world-ivy-compat/.yarn_local_cache
integration/cli-hello-world-ivy-i18n/.yarn_local_cache
integration/cli-hello-world-ivy-minimal/.yarn_local_cache
integration/cli-hello-world-lazy/.yarn_local_cache
integration/cli-hello-world-lazy-rollup/.yarn_local_cache
integration/dynamic-compiler/.yarn_local_cache
integration/hello_world__closure/.yarn_local_cache
integration/hello_world__systemjs_umd/.yarn_local_cache
integration/i18n/.yarn_local_cache
integration/injectable-def/.yarn_local_cache
integration/ivy-i18n/.yarn_local_cache
integration/language_service_plugin/.yarn_local_cache
integration/ng_elements/.yarn_local_cache
integration/ng_update/.yarn_local_cache
integration/ng_update_migrations/.yarn_local_cache
integration/ngcc/.yarn_local_cache
integration/platform-server/.yarn_local_cache
integration/service-worker-schema/.yarn_local_cache
integration/side-effects/.yarn_local_cache
integration/terser/.yarn_local_cache
integration/typings_test_ts36/.yarn_local_cache
packages/bazel/node_modules

View File

@ -62,6 +62,23 @@ test --test_output=errors
# Bazel flags for CircleCI are in /.circleci/bazel.linux.rc and /.circleci/bazel.windows.rc
##################################
# Settings for integration tests #
##################################
# Trick bazel into treating BUILD files under integration/bazel as being regular files
# This lets us glob() up all the files inside this integration test to make them inputs to tests
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
# The following environment variables need to be available for certain
# integration tests such as //integration:cli_hello_world_test
# integration/bazel-schematics uses $CI_CHROMEDRIVER_VERSION_ARG in its postinstall webdriver-manager update
run --action_env=CI_CHROMEDRIVER_VERSION_ARG
test --action_env=CI_CHROMEDRIVER_VERSION_ARG
################################
# Temporary Settings for Ivy #
################################
@ -100,7 +117,6 @@ build:remote --javabase=@rbe_ubuntu1604_angular//java:jdk
build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
build:remote --crosstool_top=@rbe_ubuntu1604_angular//cc:toolchain
build:remote --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
build:remote --extra_toolchains=@rbe_ubuntu1604_angular//config:cc-toolchain
build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
build:remote --host_platform=//tools:rbe_ubuntu1604-angular

View File

@ -285,10 +285,26 @@ jobs:
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- run:
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
no_output_timeout: 20m
test_integration_bazel:
executor:
# Needed because the //integration:bazel-schematics_test test expect Chrome to be installed
name: browsers-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- run:
# Run "exclusive" and "manual" bazel-in-bazel integration tests in their own CI job
# as they take 8m+ to execute and with bazel running inside bazel they are too
# memory intensive to be run in parallel with other tests so are tagged as "exclusive"
command: yarn bazel test //integration:bazel_test //integration:bazel-schematics_test
no_output_timeout: 20m
# Temporary job to test what will happen when we flip the Ivy flag to true
test_ivy_aot:
executor:
@ -297,6 +313,7 @@ jobs:
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# We need to explicitly specify the --symlink_prefix option because otherwise we would
# not be able to easily find the output bin directory when uploading artifacts for size
# measurements.
@ -561,7 +578,7 @@ jobs:
# of memory. Together with the system under test, this can exhaust the RAM
# on a 4G worker so we use a larger machine here too.
resource_class: xlarge
parallelism: 4
parallelism: 3
steps:
- custom_attach_workspace
- init_environment
@ -807,6 +824,9 @@ workflows:
- test:
requires:
- setup
- test_integration_bazel:
requires:
- setup
- test_ivy_aot:
requires:
- setup

View File

@ -953,6 +953,7 @@ groups:
'tools/ng_rollup_bundle/**',
'tools/ngcontainer/**',
'tools/npm/**',
'tools/npm_integration_test/**',
'tools/public_api_guard/BUILD.bazel',
'tools/public_api_guard/public_api_guard.bzl',
'tools/pullapprove/**',

View File

@ -43,8 +43,11 @@ node_repositories(
package_json = ["//:package.json"],
)
load("//integration:angular_integration_test.bzl", "npm_package_archives")
yarn_install(
name = "npm",
manual_build_file_contents = npm_package_archives(),
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)

222
integration/BUILD.bazel Normal file
View File

@ -0,0 +1,222 @@
load(":angular_integration_test.bzl", "angular_integration_test")
# Some integration ports must be managed manually to be unique and in other
# cases the tests are able to select a random free port.
#
# Where `ng e2e` is used we pass `ng e2e --port 0` which prompts the cli
# to select a random free port for the the e2e test. The protractor.conf is
# automaticaly updated to use this port.
#
# Karma automatically finds a free port so no effort is needed there.
#
# The manually configured ports are as follows:
#
# TEST PORT CONFIGURATION
# ==== ==== =============
# dynamic-compiler 4201 /e2e/browser.config.json: "port": 4201
# hello_world__closure 4202 /e2e/browser.config.json: "port": 4202
# hello_world__systemjs_umd 4203 /bs-config.e2e.json: "port": 4203
# i18n 4204 /e2e/browser.config.json: "port": 4204
# ng_elements 4205 /e2e/browser.config.json: "port": 4205
# platform-server 4206 /src/server.ts: app.listen(4206,...
[
angular_integration_test(
name = test_folder + "_test",
test_folder = test_folder,
)
for test_folder in [
"cli-hello-world",
"cli-hello-world-ivy-compat",
"cli-hello-world-ivy-minimal",
"cli-hello-world-lazy",
"cli-hello-world-lazy-rollup",
"language_service_plugin",
"ng_update",
"service-worker-schema",
"terser",
]
]
# The following tests should not be run with npm packages generated with `--config=ivy`
[
angular_integration_test(
name = test_folder + "_test",
tags = ["no-ivy-aot"],
test_folder = test_folder,
)
for test_folder in [
# `injectable-def` has .ngfactory imports:
# ```
# FAIL: Expected 'ngcc' to add build marker for 'esm2015' in '@angular/common'.
# ```
# as it is not expecting the @angular bundles to be generated with `--define=compile=aot`
"injectable-def",
# `ngcc` fails with:
# ```
# + ngc -p tsconfig-app.json
# src/main.ts(5,34): error TS2307: Cannot find module './app.ngfactory'.
# Error during template compile of 'AppModule'
# Function calls are not supported in decorators but 'BrowserModule' was called.
# ```
"ngcc",
# `ng_update_migrations` has golden tests failures such as:
# ```
# ✘ File "src/app/migration-tests/undecorated-derived-classes.ts" does not match the expected output.
# --------------------------------------------
# --- src/app/migration-tests/undecorated-derived-classes.ts Expected content
# +++ src/app/migration-tests/undecorated-derived-classes.ts Actual content
# @@ -1,50 +1,19 @@
# -import { Directive, NgModule, NgZone, Component } from '@angular/core';
# -import { CheckboxControlValueAccessor, NG_VALUE_ACCESSOR, NG_ASYNC_VALIDATORS } from '@angular/forms';
# -import { BaseComponentFromOtherFile, hostBindings } from './base-component';
# +import {Directive, NgModule, NgZone} from '@angular/core';
# +import {CheckboxControlValueAccessor} from '@angular/forms';
# +import {BaseComponentFromOtherFile} from './base-component';
# ```
"ng_update_migrations",
# `i18n` has .ngfactory imports:
# ```
# $ ngc && yarn run closure && concurrently "yarn run serve" "yarn run protractor" --kill-others --success first && npm run test-locale-folder
# src/main.ts(2,34): error TS2307: Cannot find module './app.ngfactory'.
# Unexpected value 'BrowserModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-58883IJeiUMLGHRRQ/node_modules/@angular/platform-browser/src/browser.d.ts' imported by the module 'AppModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-58883IJeiUMLGHRRQ/src/app.ts'. Please add a @NgModule annotation.
# ```
"i18n",
# `ivy-i18n` fails with:
# ```
# ERROR in Unexpected value 'BrowserModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-59999cYHm5rQoUk0v/node_modules/@angular/platform-browser/src/browser.d.ts' imported by the module 'AppModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-59999cYHm5rQoUk0v/src/app/app.module.ts'. Please add a @NgModule annotation.
# @angular/core/src/i18n/tokens.ts(31,22): Error during template compile of 'LOCALE_ID'
# Only initialized variables and constants can be referenced in decorators because the value of this variable is needed by the template compiler.
# Can't resolve all parameters for AppComponent in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-59999cYHm5rQoUk0v/src/app/app.component.ts: (?).
# The pipe 'percent' could not be found ("
# </div>
# <p id="locale">{{ locale }}</p>
# <p id="pipe">{{ [ERROR ->]1 | percent }} awesome</p>
# <p id="date">{{ jan | date : 'LLLL' }}</p>
# <h2>Here are some links to help")
# The pipe 'date' could not be found ("
# <p id="locale">{{ locale }}</p>
# <p id="pipe">{{ 1 | percent }} awesome</p>
# <p id="date">{{ [ERROR ->]jan | date : 'LLLL' }}</p>
# <h2>Here are some links to help you start: </h2>
# <ul>
# ")
# ```
"ivy-i18n",
# `cli-hello-world-ivy-i18n` fails with:
# ```
# ERROR in Unexpected value 'BrowserModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-59695c6NwKC6Djcv4/node_modules/@angular/platform-browser/src/browser.d.ts' imported by the module 'AppModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-59695c6NwKC6Djcv4/src/app/app.module.ts'. Please add a @NgModule annotation.
# @angular/core/src/i18n/tokens.ts(31,22): Error during template compile of 'LOCALE_ID'
# Only initialized variables and constants can be referenced in decorators because the value of this variable is needed by the template compiler.
# Can't resolve all parameters for AppComponent in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-59695c6NwKC6Djcv4/src/app/app.component.ts: (?).
# The pipe 'percent' could not be found ("1 i18n="some:description"> Hello {{ title }}! </h1>
# <p id="locale">{{ locale }}</p>
# <p id="pipe">{{ [ERROR ->]1 | percent }} awesome</p>
# <p id="date">{{ jan | date : 'LLLL' }}</p>
# ")
# The pipe 'date' could not be found ("
# <p id="locale">{{ locale }}</p>
# <p id="pipe">{{ 1 | percent }} awesome</p>
# <p id="date">{{ [ERROR ->]jan | date : 'LLLL' }}</p>
# ")
# ```
"cli-hello-world-ivy-i18n",
# `dynamic-compiler` has .ngfactory imports:
# ```
# $ ngc && yarn run closure && concurrently "yarn run serve" "yarn run protractor" --kill-others --success first
# src/main.ts(2,34): error TS2307: Cannot find module './app.ngfactory'.
# Unexpected value 'BrowserModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-620291x2ZHDyIWIdL/node_modules/@angular/platform-browser/src/browser.d.ts' imported by the module 'AppModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-620291x2ZHDyIWIdL/src/app.ts'. Please add a @NgModule annotation.
# ```
"dynamic-compiler",
# `ng_elements` has .ngfactory imports:
# ```
# $ ngc && yarn run closure && concurrently "yarn run serve" "yarn run protractor" --kill-others --success first
# src/main.ts(2,34): error TS2307: Cannot find module './app.ngfactory'.
# Unexpected value 'BrowserModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-620291x2ZHDyIWIdL/node_modules/@angular/platform-browser/src/browser.d.ts' imported by the module 'AppModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-620291x2ZHDyIWIdL/src/app.ts'. Please add a @NgModule annotation.
# ```
"ng_elements",
# `platform-server` has .ngfactory imports:
# ```
# src/server.ts(15,47): error TS2307: Cannot find module './helloworld/app.server.ngfactory'.
# src/server.ts(18,50): error TS2307: Cannot find module './transferstate/app.server.ngfactory'.
# src/helloworld/client.ts(13,41): error TS2307: Cannot find module './app.ngfactory'.
# src/transferstate/client.ts(13,44): error TS2307: Cannot find module './app.ngfactory'.
# Error during template compile of 'HelloWorldModule'
# Function calls are not supported in decorators but 'BrowserModule' was called.
# ```
"platform-server",
# `side-effects` fails on minor differences to snapshots:
# ```
# The following snapshots have changed:
# Index: /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-629425HECGefnB0O7/snapshots/animations-browser/esm5.js
# ===================================================================
# --- /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-629425HECGefnB0O7/snapshots/animations-browser/esm5.js
# +++ /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-629425HECGefnB0O7/snapshots/animations-browser/esm5.js
# @@ -1,5 +1,5 @@
# -import "tslib";
# -
# import "@angular/animations";
#
# import "@angular/core";
# +
# +import "tslib";
# ```
"side-effects",
# `hello_world__closure` has .ngfactory imports:
# ```
# $ ngc && yarn run closure && concurrently "yarn run serve" "yarn run protractor" --kill-others --success first
# src/main.ts(2,34): error TS2307: Cannot find module './app.ngfactory'.
# Unexpected value 'BrowserModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-67554cqUv301iuuwu/node_modules/@angular/platform-browser/src/browser.d.ts' imported by the module 'AppModule in /private/var/folders/0l/nj_q9kzj49gdz1w6f5v9tw3h0000gn/T/tmp-67554cqUv301iuuwu/src/app.ts'. Please add a @NgModule annotation. # ```
"hello_world__closure",
]
]
# Special cases for bazel-in-bazel tests which are tagged as "exclusive" so that they don't drain
# all the resources on the machine when run in parallel. Also only run these tests against the
# ViewEngine generated packages. Since these tests are "exclusive", also tag them as manual as they
# will be run in separate CI jobs.
angular_integration_test(
name = "bazel_test",
tags = [
"exclusive",
"manual",
"no-ivy-aot",
],
test_folder = "bazel",
)
angular_integration_test(
name = "bazel-schematics_test",
# integration/bazel-schematics uses $CI_CHROMEDRIVER_VERSION_ARG in its postinstall webdriver-manager update
# TODO(gregmagolan): remove that dependency have that test use puppeteer as well
configuration_env_vars = [
"CI_CHROMEDRIVER_VERSION_ARG",
],
tags = [
"exclusive",
"manual",
"no-ivy-aot",
# Don't execute remotely as these test depends on the locally installed Chrome
"no-remote-exec",
],
test_folder = "bazel-schematics",
)
# Special case for `typings_test_ts36` test as we want to pin
# `typescript` at version 3.6.x for that test and not link to the
# root @npm//typescript package.
angular_integration_test(
name = "typings_test_ts36_test",
pinned_npm_packages = ["typescript"],
test_folder = "typings_test_ts36",
)
# Special case for `hello_world__systemjs_umd` test as we want to pin
# `systems` at version 0.20.2 and not link to the the root @npm//systemjs
# which is stuck at 0.18.10 and can't be updated to 0.20.2 without
# breaking the legacy saucelabs job.
angular_integration_test(
name = "hello_world__systemjs_umd_test",
pinned_npm_packages = ["systemjs"],
test_folder = "hello_world__systemjs_umd",
)

View File

@ -65,6 +65,35 @@ $ ./integration/run_tests.sh
The test runner will first re-build any stale npm packages, then `cd` into each subdirectory to
execute the test.
## Running integration tests under Bazel
The PR https://github.com/angular/angular/pull/33927 added the ability to run integration tests with Bazel. These tests can be resource intensive so it is recommended to limit the number of concurrent test jobs with the `--local_test_jobs` bazel flag.
Locally, if Bazel uses all of your cores to run the maximum number of integration tests in parallel then this can lead to test timeouts and flakes and freeze up your machine while these tests are running. You can limit the number of concurrent local integration tests that run with:
```
yarn bazel test --local_test_jobs=<N> //integration/...
```
Set a reasonable `local_test_jobs` limit for your local machine to prevent full cpu utilization during local development test runs.
To avoid having to specify this command line flag, you may want to include it in your `.bazelrc.user` file:
```
test --local_test_jobs=<N>
```
The downside of this is that this will apply to all tests and not just the resource intensive integration tests.
### Bazel-in-bazel integration tests
Two of the integration tests that run Bazel-in-Bazel are particularly resource intensive and are tagged "manual" and "exclusive". To run these tests use,
```
yarn bazel test //integration:bazel_test
yarn bazel test //integration:bazel-schematics_test
```
## Browser tests
For integration tests we use the puppeteer provisioned version of Chrome. For both Karma and Protractor tests we set a number of browser testing flags. To avoid duplication, they will be listed and explained here and the code will reference this file for more information.

View File

@ -0,0 +1,153 @@
# Copyright Google Inc. All Rights Reserved.
#
# Use of this source code is governed by an MIT-style license that can be
# found in the LICENSE file at https://angular.io/license
"""Angular integration testing
"""
load("//tools/npm_integration_test:npm_integration_test.bzl", "npm_integration_test")
# The @npm packages at the root node_modules are used by integration tests
# with `file:../../node_modules/foobar` references
NPM_PACKAGE_ARCHIVES = [
"check-side-effects",
"core-js",
"jasmine",
"typescript",
"rxjs",
"systemjs",
"tsickle",
"tslib",
"protractor",
"puppeteer",
"rollup",
"rollup-plugin-commonjs",
"rollup-plugin-node-resolve",
"webdriver-manager",
"@angular/cli",
"@angular-devkit/build-angular",
"@bazel/bazel",
"@types/jasmine",
"@types/jasminewd2",
"@types/node",
]
# The generated npm packages should ALWAYS be replaced in integration tests
# so we pass them to the `check_npm_packages` attribute of npm_integration_test
GENERATED_NPM_PACKAGES = [
"@angular/animations",
"@angular/bazel",
"@angular/benchpress",
"@angular/common",
"@angular/compiler",
"@angular/compiler-cli",
"@angular/core",
"@angular/elements",
"@angular/forms",
"@angular/http",
"@angular/language-service",
"@angular/localize",
"@angular/platform-browser",
"@angular/platform-browser-dynamic",
"@angular/platform-server",
"@angular/platform-webworker",
"@angular/platform-webworker-dynamic",
"@angular/router",
"@angular/service-worker",
"@angular/upgrade",
"zone.js",
]
def npm_package_archives():
"""Function to generate pkg_tar definitions for WORKSPACE yarn_install manual_build_file_contents"""
npm_packages_to_archive = NPM_PACKAGE_ARCHIVES
result = """load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
"""
for name in npm_packages_to_archive:
label_name = _npm_package_archive_label(name)
last_segment_name = name if name.find("/") == -1 else name.split("/")[-1]
result += """pkg_tar(
name = "{label_name}",
srcs = ["//{name}:{last_segment_name}__all_files"],
extension = "tar.gz",
strip_prefix = "./node_modules/{name}",
# should not be built unless it is a dependency of another rule
tags = ["manual"],
)
""".format(name = name, label_name = label_name, last_segment_name = last_segment_name)
return result
def _npm_package_archive_label(package_name):
return package_name.replace("/", "_").replace("@", "") + "_archive"
def _angular_integration_test(pinned_npm_packages = [], **kwargs):
"Set defaults for the npm_integration_test common to the angular repo"
commands = kwargs.pop("commands", None)
if not commands:
# By default run `yarn install` followed by `yarn test` using
# the bazel managed hermetic version of yarn inside
commands = [
# Workaround https://github.com/yarnpkg/yarn/issues/2165
# Yarn will cache file://dist URIs and not update Angular code
"rm -rf ./.yarn_local_cache",
"mkdir .yarn_local_cache",
"patch-package-json",
"$(rootpath @nodejs//:yarn_bin) install --cache-folder ./.yarn_local_cache",
"$(rootpath @nodejs//:yarn_bin) test",
"rm -rf ./.yarn_local_cache",
]
# Complete list of npm packages to override in the test's package.json file mapped to
# tgz archive to use for the replacement. This is the full list for all integration
# tests. Any given integration does not need to use all of these packages.
npm_packages = {}
for name in NPM_PACKAGE_ARCHIVES:
if name not in pinned_npm_packages:
npm_packages["@npm//:" + _npm_package_archive_label(name)] = name
for name in GENERATED_NPM_PACKAGES:
last_segment_name = name if name.find("/") == -1 else name.split("/")[-1]
npm_packages["//packages/%s:npm_package_archive" % last_segment_name] = name
npm_integration_test(
check_npm_packages = GENERATED_NPM_PACKAGES,
commands = commands,
npm_packages = npm_packages,
tags = kwargs.pop("tags", []) + [
# `integration` tag is used for filtering out these tests from the normal
# developer workflow
"integration",
# Integration do not work inside of a sandbox as they may run host applications such
# as chrome (which is run by ng) that require access to files outside of the sandbox.
"no-sandbox",
],
data = kwargs.pop("data", []) + [
# We need the yarn_bin & yarn_files available at runtime
"@nodejs//:yarn_bin",
"@nodejs//:yarn_files",
],
# 15-minute timeout
timeout = "long",
# Tells bazel that this test should be allocated a large amount of memory.
# See https://docs.bazel.build/versions/2.0.0/be/common-definitions.html#common-attributes-tests.
size = "enormous",
**kwargs
)
def angular_integration_test(name, test_folder, pinned_npm_packages = [], **kwargs):
"Sets up the integration test target based on the test folder name"
native.filegroup(
name = "_%s_sources" % name,
srcs = native.glob(
include = ["%s/**" % test_folder],
exclude = [
"%s/node_modules/**" % test_folder,
"%s/.yarn_local_cache/**" % test_folder,
],
),
)
_angular_integration_test(
name = name,
test_files = kwargs.pop("test_files", "_%s_sources" % name),
pinned_npm_packages = pinned_npm_packages,
**kwargs
)

View File

@ -4,9 +4,23 @@
"main": "index.js",
"license": "MIT",
"devDependencies": {
"@angular-devkit/build-angular": "file:../../node_modules/@angular-devkit/build-angular",
"@angular/animations": "file:../../dist/packages-dist/animations",
"@angular/bazel": "file:../../dist/packages-dist/bazel",
"@angular/cli": "file:../../node_modules/@angular/cli",
"@bazel/bazel": "file:../../node_modules/@bazel/bazel"
"@angular/common": "file:../../node_modules/@angular/common",
"@angular/compiler": "file:../../node_modules/@angular/compiler",
"@angular/compiler-cli": "file:../../node_modules/@angular/compiler-cli",
"@angular/core": "file:../../node_modules/@angular/core",
"@angular/forms": "file:../../node_modules/@angular/forms",
"@angular/language-service": "file:../../node_modules/@angular/language-service",
"@angular/platform-browser": "file:../../node_modules/@angular/platform-browser",
"@angular/platform-browser-dynamic": "file:../../node_modules/@angular/platform-browser-dynamic",
"@angular/router": "file:../../node_modules/@angular/router",
"@bazel/bazel": "file:../../node_modules/@bazel/bazel",
"@types/node": "file:../../node_modules/@types/node",
"typescript": "file:../../node_modules/typescript",
"tslib": "file:../../node_modules/tslib"
},
"scripts": {
"test": "./test.sh"

View File

@ -2,6 +2,17 @@
set -eux -o pipefail
# sedi makes `sed -i` work on both OSX & Linux
# See https://stackoverflow.com/questions/2320564/i-need-my-sed-i-command-for-in-place-editing-to-work-with-both-gnu-sed-and-bsd
function sedi () {
case $(uname) in
Darwin*) sedi=('-i' '') ;;
*) sedi='-i' ;;
esac
sed "${sedi[@]}" "$@"
}
function installLocalPackages() {
# Install Angular packages that are built locally from HEAD.
# This also gets around the bug whereby yarn caches local `file://` urls.
@ -13,17 +24,24 @@ function installLocalPackages() {
)
local local_packages=()
for package in "${packages[@]}"; do
local_packages+=("@angular/${package}@file:${pwd}/../../../dist/packages-dist/${package}")
local_packages+=("@angular/${package}@file:${pwd}/../node_modules/@angular/${package}")
done
# keep typescript, tslib, and @types/node versions in sync with the ones used in this repo
local_packages+=("typescript@file:${pwd}/../../../node_modules/typescript")
local_packages+=("tslib@file:${pwd}/../../../node_modules/tslib")
local_packages+=("@types/node@file:${pwd}/../../../node_modules/@types/node")
local_packages+=("typescript@file:${pwd}/../node_modules/typescript")
local_packages+=("tslib@file:${pwd}/../node_modules/tslib")
local_packages+=("@types/node@file:${pwd}/../node_modules/@types/node")
yarn add --ignore-scripts --silent "${local_packages[@]}"
}
function patchKarmaConfForHeadless() {
sedi "s#browsers\: \['Chrome'\],#customLaunchers\: \{ ChromeHeadlessNoSandbox\: \{ base\: 'ChromeHeadless', flags\: \['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'\] \} \}, browsers\: \['ChromeHeadlessNoSandbox'\],#" ./karma.conf.js
}
function patchProtractorConfForHeadless() {
sedi "s#browserName\: 'chrome'#browserName\: 'chrome', chromeOptions\: \{ args: \['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'\] \},#" ./e2e/protractor.conf.js
}
function testBazel() {
# Set up
@ -33,6 +51,8 @@ function testBazel() {
# Create project
ng new demo --collection=@angular/bazel --routing --skip-git --skip-install --style=scss
cd demo
patchKarmaConfForHeadless
patchProtractorConfForHeadless
installLocalPackages
ng generate component widget --style=css
ng build
@ -52,13 +72,13 @@ function testNonBazel() {
# disable CLI's version check (if version is 0.0.0, then no version check happens)
yarn --cwd node_modules/@angular/cli version --new-version 0.0.0 --no-git-tag-version
# re-add build-angular
yarn add --dev file:../../../node_modules/@angular-devkit/build-angular
yarn add --dev file:../node_modules/@angular-devkit/build-angular
# TODO: Find a way to use the Chrome version provided by `puppeteer` as the rest of the
# integration projects. See https://github.com/angular/angular/pull/35049 for details.
yarn webdriver-manager update --gecko=false --standalone=false --versions.chrome=79.0.3945.130;
ng build --progress=false
ng test --progress=false --watch=false
ng e2e --configuration=production --webdriver-update=false
ng e2e --port 0 --configuration=production --webdriver-update=false
}
testBazel

View File

@ -4,7 +4,7 @@
"license": "MIT",
"scripts": {
"build": "ng build --prod",
"e2e": "ng e2e",
"e2e": "ng e2e --port 0",
"lint": "ng lint",
"ng": "ng",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
@ -31,8 +31,8 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "3.4.6",
"@types/jasminewd2": "2.0.6",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"codelyzer": "5.2.0",
"jasmine-core": "3.5.0",

View File

@ -4,13 +4,13 @@
"license": "MIT",
"scripts": {
"build": "ng build --prod",
"e2e": "ng e2e",
"e2e": "ng e2e --port 0",
"lint": "ng lint",
"ng": "ng",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
"start": "ng serve",
"pretest": "ng version",
"test": "ng e2e --prod && ng xi18n && yarn translate && ng e2e --configuration fr && ng e2e --configuration de",
"test": "ng e2e --port 0 --prod && ng xi18n && yarn translate && ng e2e --port 0 --configuration fr && ng e2e --port 0 --configuration de",
"translate": "cp src/locale/messages.xlf src/locale/messages.fr.xlf && cp src/locale/messages.xlf src/locale/messages.de.xlf && sed -i.bak -e 's/source>/target>/g' -e 's/Hello/Bonjour/' src/locale/messages.fr.xlf && sed -i.bak -e 's/source>/target>/g' -e 's/Hello/Hallo/' src/locale/messages.de.xlf",
"serve": "serve --no-clipboard --listen 4200"
},
@ -35,8 +35,8 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "3.4.4",
"@types/jasminewd2": "2.0.8",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"codelyzer": "5.2.0",
"jasmine-core": "3.5.0",

View File

@ -4,7 +4,7 @@
"license": "MIT",
"scripts": {
"build": "ng build --prod",
"e2e": "ng e2e",
"e2e": "ng e2e --port 0",
"lint": "ng lint",
"ng": "ng",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
@ -31,8 +31,8 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "3.4.6",
"@types/jasminewd2": "2.0.6",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"codelyzer": "5.2.0",
"jasmine-core": "3.5.0",

View File

@ -3,7 +3,7 @@
"version": "0.0.0",
"scripts": {
"build": "ng build --prod",
"e2e": "ng e2e --prod",
"e2e": "ng e2e --port 0 --prod",
"test": "yarn e2e && yarn build && node check-output-for-ngdevmode.js",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
},
@ -18,7 +18,7 @@
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/router": "file:../../dist/packages-dist/router",
"rxjs": "file:../../node_modules/rxjs",
"tslib": "1.10.0",
"tslib": "file:../../node_modules/tslib",
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
@ -26,9 +26,9 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/node": "10.14.21",
"@types/jasmine": "3.4.4",
"@types/jasminewd2": "2.0.8",
"@types/node": "file:../../node_modules/@types/node",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"codelyzer": "5.1.2",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",

View File

@ -3,7 +3,7 @@
"version": "0.0.0",
"scripts": {
"build": "ng build --prod",
"e2e": "ng e2e --prod",
"e2e": "ng e2e --port 0 --prod",
"test": "yarn e2e && yarn build && node check-output-for-ngdevmode.js",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
},
@ -18,7 +18,7 @@
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/router": "file:../../dist/packages-dist/router",
"rxjs": "file:../../node_modules/rxjs",
"tslib": "1.10.0",
"tslib": "file:../../node_modules/tslib",
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
@ -26,9 +26,9 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/node": "10.14.21",
"@types/jasmine": "3.4.4",
"@types/jasminewd2": "2.0.8",
"@types/node": "file:../../node_modules/@types/node",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"codelyzer": "5.1.2",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",

View File

@ -8,7 +8,7 @@
"pretest": "ng version",
"test": "ng test && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production",
"lint": "ng lint",
"e2e": "ng e2e",
"e2e": "ng e2e --port 0",
"postinstall": "ngcc --properties es2015 --create-ivy-entry-points"
},
"private": true,
@ -30,8 +30,8 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "3.4.6",
"@types/jasminewd2": "2.0.8",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"codelyzer": "5.2.0",
"jasmine-core": "3.5.0",

View File

@ -1,7 +1,7 @@
{
"open": false,
"logLevel": "silent",
"port": 8080,
"port": 4201,
"server": {
"baseDir": ".",
"routes": {

View File

@ -13,7 +13,8 @@ exports.config = {
}
},
directConnect: true,
baseUrl: 'http://localhost:8080/',
// Port comes from lite-server config `/e2e/browser.config.json` `"port": 4201`
baseUrl: 'http://localhost:4201/',
framework: 'jasmine',
useAllAngular2AppRoots: true
};

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ const path = require('path');
const minimist = require('minimist');
// Parsed command line arguments.
const {shardIndex, maxShards} = minimist(process.argv.slice(2));
const {_, shardIndex, maxShards} = minimist(process.argv.slice(2));
// Ensure that all CLI options are set properly.
if (shardIndex == null) {
@ -32,46 +32,11 @@ if (shardIndex == null) {
} else if (maxShards == null) {
throw new Error('The "--maxShards" option has not been specified.');
}
// List of all integration tests that are available.
const integrationTests = fs.readdirSync(__dirname).filter(
testName => fs.statSync(path.join(__dirname, testName)).isDirectory());
// Manual test shards which aren't computed automatically. This is helpful when a specific
// set of integration test takes up *way* more time than all other tests, and we want to
// balance out the duration for all specific shards.
const manualTestShards = [
// The first shard should only run the bazel integration tests because these take up
// a lot of time and shouldn't be split up automatically.
['bazel', 'bazel-schematics']
];
// Tests which haven't been assigned manually to a shard. These tests will be automatically
// split across the remaining available shards.
const unassignedTests = stripManualOverrides(integrationTests, manualTestShards);
if (manualTestShards.length === maxShards && unassignedTests.length) {
throw new Error(
`Tests have been specified manually for all available shards, but there were ` +
`integration tests which haven't been specified and won't run right now. Missing ` +
`tests: ${unassignedTests.join(', ')}`)
} else if (manualTestShards.length > maxShards) {
throw new Error(
`Too many manual shards have been specified. Increase the amount of maximum shards.`);
if (shardIndex >= maxShards) {
throw new Error('shardIndex out of bounds');
}
// In case the shard for the current index has been specified manually, we just output
// the tests for the manual shard.
if (manualTestShards[shardIndex]) {
printTestNames(manualTestShards[shardIndex]);
} else {
const amountManualShards = manualTestShards.length;
// In case there isn't a manual shard specified for this shard index, we just compute the
// tests for this shard. Note that we need to subtract the amount of manual shards because
// we need to split up the unassigned tests across the remaining available shards.
printTestNames(getTestsForShardIndex(
unassignedTests, shardIndex - amountManualShards, maxShards - amountManualShards));
}
printTestNames(getTestsForShardIndex(_, shardIndex, maxShards));
/**
* Splits the specified tests into a limited amount of shards and returns the tests that should
@ -82,16 +47,6 @@ function getTestsForShardIndex(tests, shardIndex, maxShards) {
return tests.filter((n, index) => index % maxShards === shardIndex);
}
/**
* Strips all manual tests from the list of integration tests. This is necessary because
* when computing the shards automatically we don't want to include manual tests again. This
* would mean that CircleCI runs some integration tests multiple times.
*/
function stripManualOverrides(integrationTests, manualShards) {
const allManualTests = manualShards.reduce((res, manualTests) => res.concat(manualTests), []);
return integrationTests.filter(testName => !allManualTests.includes(testName))
}
/** Prints the specified test names to the stdout. */
function printTestNames(testNames) {
// Print the test names joined with spaces because this allows Bash to easily convert the output

View File

@ -1,7 +1,7 @@
{
"open": false,
"logLevel": "silent",
"port": 8080,
"port": 4202,
"server": {
"baseDir": "src",
"routes": {

View File

@ -13,7 +13,8 @@ exports.config = {
}
},
directConnect: true,
baseUrl: 'http://localhost:8080/',
// Port comes from lite-server config `/e2e/browser.config.json` `"port": 4202`
baseUrl: 'http://localhost:4202/',
framework: 'jasmine',
useAllAngular2AppRoots: true
};

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"outDir": "../built/e2e",
"types": ["jasmine"],
"types": ["jasmine", "jasminewd2"],
// TODO(alexeagle): was required for Protractor 4.0.11
"skipLibCheck": true
}

View File

@ -16,11 +16,13 @@
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
"@types/jasmine": "2.5.41",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"concurrently": "3.4.0",
"lite-server": "2.2.2",
"protractor": "file:../../node_modules/protractor",
"puppeteer": "file:../../node_modules/puppeteer"
"puppeteer": "file:../../node_modules/puppeteer",
"tsickle": "file:../../node_modules/tsickle"
},
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",
"resolutions": {

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"open": false,
"logLevel": "silent",
"port": 8000,
"port": 4203,
"server": {
"baseDir": "src",
"routes": {

View File

@ -4,8 +4,6 @@
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"skipLibCheck": true,
"types": [
"jasmine"
]
"types": ["jasmine", "jasminewd2"]
}
}

View File

@ -23,7 +23,8 @@
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
"@types/jasmine": "2.5.41",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"concurrently": "3.4.0",
"lite-server": "2.2.2",
"protractor": "file:../../node_modules/protractor",

View File

@ -13,7 +13,8 @@ exports.config = {
}
},
directConnect: true,
baseUrl: 'http://localhost:8000/',
// Port comes from lite-serve config `/bs-config.e2e.json` `"port": 4203`
baseUrl: 'http://localhost:4203/',
framework: 'jasmine',
useAllAngular2AppRoots: true,
};

View File

@ -1,7 +1,7 @@
{
"open": false,
"logLevel": "silent",
"port": 8080,
"port": 4204,
"server": {
"baseDir": "src",
"routes": {

View File

@ -13,7 +13,8 @@ exports.config = {
}
},
directConnect: true,
baseUrl: 'http://localhost:8080/',
// Port comes from lite-server config `/e2e/browser.config.json` `"port": 4204`
baseUrl: 'http://localhost:4204/',
framework: 'jasmine',
useAllAngular2AppRoots: true
};

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"outDir": "../built/e2e",
"types": ["jasmine"],
"types": ["jasmine", "jasminewd2"],
// TODO(alexeagle): was required for Protractor 4.0.11
"skipLibCheck": true
}

View File

@ -17,11 +17,13 @@
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
"@types/jasmine": "2.5.41",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"concurrently": "3.4.0",
"lite-server": "2.2.2",
"protractor": "file:../../node_modules/protractor",
"puppeteer": "file:../../node_modules/puppeteer"
"puppeteer": "file:../../node_modules/puppeteer",
"tsickle": "file:../../node_modules/tsickle"
},
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",
"resolutions": {

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
"@types/jasmine": "2.5.41",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"concurrently": "3.4.0",
"lite-server": "2.2.2",
"protractor": "file:../../node_modules/protractor"

View File

@ -4,7 +4,7 @@
"license": "MIT",
"scripts": {
"build": "ng build --prod",
"e2e": "ng e2e",
"e2e": "ng e2e --port 0",
"lint": "ng lint",
"ng": "ng",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
@ -49,8 +49,8 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "3.4.4",
"@types/jasminewd2": "2.0.8",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"codelyzer": "5.2.0",
"jasmine-core": "3.5.0",

View File

@ -6,6 +6,7 @@
"dependencies": {
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/node": "file:../../node_modules/@types/node",
"jasmine": "file:../../node_modules/jasmine",
"typescript": "file:../../node_modules/typescript"

View File

@ -1,7 +1,7 @@
{
"open": false,
"logLevel": "silent",
"port": 8080,
"port": 4205,
"server": {
"baseDir": "src",
"routes": {

View File

@ -13,7 +13,8 @@ exports.config = {
}
},
directConnect: true,
baseUrl: 'http://localhost:8080/',
// Port comes from lite-server config `/e2e/browser.config.json` `"port": 4205`
baseUrl: 'http://localhost:4205/',
framework: 'jasmine',
useAllAngular2AppRoots: true
};

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"outDir": "../built/e2e",
"types": ["jasmine"],
"types": ["jasmine", "jasminewd2"],
// TODO(alexeagle): was required for Protractor 4.0.11
"skipLibCheck": true
}

View File

@ -17,11 +17,13 @@
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
"@types/jasmine": "2.5.41",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"concurrently": "3.4.0",
"lite-server": "2.2.2",
"protractor": "file:../../node_modules/protractor",
"puppeteer": "file:../../node_modules/puppeteer"
"puppeteer": "file:../../node_modules/puppeteer",
"tsickle": "file:../../node_modules/tsickle"
},
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",
"resolutions": {

File diff suppressed because it is too large Load Diff

View File

@ -28,8 +28,8 @@
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "3.3.16",
"@types/jasminewd2": "2.0.6",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"chalk": "2.4.2",
"diff": "4.0.1",

View File

@ -21,7 +21,7 @@
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
"@types/jasmine": "2.5.41",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"concurrently": "3.4.0",
"lite-server": "2.2.2",
"protractor": "file:../../node_modules/protractor"

View File

@ -184,6 +184,9 @@ assertSucceeded "Expected 'ngcc' to log 'Compiling'."
# Can it compile `@angular/platform-server` in UMD + typings without errors?
# (The CLI prefers the `main` property (which maps to UMD) over `module` when compiling `@angular/platform-server`.
# See https://github.com/angular/angular-cli/blob/e36853338/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/server.ts#L34)
if [[ -z "$cache" ]]; then
cache=".yarn_local_cache"
fi
rm -rf node_modules/@angular/platform-server && \
yarn install --cache-folder $cache --check-files && \
test -d node_modules/@angular/platform-server

View File

@ -18,7 +18,8 @@ exports.config = {
}
},
directConnect: true,
baseUrl: 'http://localhost:9876/',
// Port comes from express config `/src/server.ts` `app.listen(4206,...`
baseUrl: 'http://localhost:4206/',
framework: 'jasmine',
useAllAngular2AppRoots: true
};

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"outDir": "../built/e2e",
"types": ["jasmine"],
"types": ["jasmine", "jasminewd2"],
// TODO(alexeagle): was required for Protractor 4.0.11
"skipLibCheck": true
}

View File

@ -22,7 +22,8 @@
"zone.js": "file:../../dist/zone.js-dist/zone.js"
},
"devDependencies": {
"@types/jasmine": "2.5.41",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"babel-core": "6.26.3",
"babel-loader": "6.4.1",
@ -31,6 +32,7 @@
"protractor": "file:../../node_modules/protractor",
"puppeteer": "file:../../node_modules/puppeteer",
"raw-loader": "0.5.1",
"tsickle": "file:../../node_modules/tsickle",
"webpack": "2.7.0"
},
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",

View File

@ -41,4 +41,4 @@ app.get('/favicon.ico', (req, res) => { res.send(''); });
app.get('/helloworld', render(HelloWorldServerModuleNgFactory, helloworld));
app.get('/transferstate', render(TransferStateServerModuleNgFactory, transferstate));
app.listen(9876, function() { console.log('Server listening on port 9876!'); });
app.listen(4206, function() { console.log('Server listening on port 4206!'); });

View File

@ -9,7 +9,12 @@ cd "$(dirname "$0")"
# basedir is the workspace root
readonly basedir=$(pwd)/..
readonly bazel_bin=$(yarn bin)/bazel
# Skip all integration tests that are now handled by angular_integration_test except
# the tests that are tracked for payload size; these are:
# - cli-hello-world*
# - hello_world__closure
readonly TEST_DIRS=$(find $(ls) -maxdepth 0 -type d \( -name "cli-hello-world*" -or -name "hello_world__closure" \))
# When running on the CI, we track the payload size of various integration output files. Also
# we shard tests across multiple CI job instances. The script needs to be run with a shard index
@ -18,11 +23,11 @@ readonly bazel_bin=$(yarn bin)/bazel
if $CI; then
source ${basedir}/scripts/ci/payload-size.sh
SHARD_INDEX=${1:?"No shard index has been specified."}
MAX_SHARDS=${2:?"The maximum amount of shards has not been specified."}
readonly SHARD_INDEX=${1:?"No shard index has been specified."}
readonly MAX_SHARDS=${2:?"The maximum amount of shards has not been specified."}
# Determines the tests that need to be run for this shard index.
TEST_DIRS=$(node ./get-sharded-tests.js --shardIndex ${SHARD_INDEX} --maxShards ${MAX_SHARDS})
readonly RUN_TESTS=$(node ./get-sharded-tests.js --shardIndex ${SHARD_INDEX} --maxShards ${MAX_SHARDS} ${TEST_DIRS})
# NB: we don't run build-packages-dist.js because we expect that it was done
# by an earlier job in the CircleCI workflow.
@ -33,9 +38,11 @@ else
# If we aren't running on CircleCI, we do not shard tests because this would be the job of
# Bazel eventually. For now, we just run all tests sequentially when running locally.
TEST_DIRS=$(ls | grep -v node_modules)
readonly RUN_TESTS=${TEST_DIRS}
fi
echo "Running integration tests: ${RUN_TESTS}"
# Workaround https://github.com/yarnpkg/yarn/issues/2165
# Yarn will cache file://dist URIs and not update Angular code
export readonly cache=.yarn_local_cache
@ -46,11 +53,14 @@ rm_cache
mkdir $cache
trap rm_cache EXIT
for testDir in ${TEST_DIRS}; do
for testDir in ${RUN_TESTS}; do
[[ -d "$testDir" ]] || continue
echo "#################################"
echo ""
echo "######################################################################"
echo "Running integration test $testDir"
echo "#################################"
echo "######################################################################"
(
cd $testDir
rm -rf dist

View File

@ -11,6 +11,7 @@
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"rxjs": "file:../../node_modules/rxjs",
"terser": "3.17.0",
"typescript": "file:../../node_modules/typescript",
"zone.js": "file:../../dist/zone.js-dist/zone.js"
}
}

View File

@ -6,7 +6,7 @@
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../../dist/typings_test_ts36/",
"outDir": "./dist/out-tsc",
"rootDir": ".",
"target": "es5",
"lib": [

View File

@ -35,6 +35,7 @@
"// 1": "dependencies are used locally and by bazel",
"dependencies": {
"@angular-devkit/architect": "0.900.3",
"@angular-devkit/build-angular": "0.900.3",
"@angular-devkit/build-optimizer": "0.900.3",
"@angular-devkit/core": "9.0.3",
"@angular-devkit/schematics": "9.0.3",
@ -134,7 +135,6 @@
"// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.",
"// 3": "when updating @bazel/bazel version you also need to update the RBE settings in .bazelrc (see https://github.com/angular/angular/pull/27935)",
"devDependencies": {
"@angular-devkit/build-angular": "0.900.3",
"@angular/cli": "9.0.3",
"@bazel/bazel": "2.1.0",
"@bazel/buildifier": "^0.29.0",

View File

@ -29,6 +29,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/compiler/test:__pkg__",
],

View File

@ -25,7 +25,7 @@ pkg_npm(
tags = ["release-with-framework"],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = ["//integration:__pkg__"],
deps = [
"//packages/bazel/src/api-extractor:lib",
"//packages/bazel/src/builders",

View File

@ -26,7 +26,7 @@ pkg_npm(
],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = ["//integration:__pkg__"],
deps = [
":benchpress",
],

View File

@ -33,6 +33,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/bazel/test/ng_package:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/compiler-cli/ngcc/test:__pkg__",

View File

@ -51,6 +51,7 @@ pkg_npm(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
],
deps = [

View File

@ -26,6 +26,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/language-service/test:__pkg__",
],

View File

@ -39,6 +39,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/bazel/test/ng_package:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/compiler-cli/ngcc/test:__pkg__",

View File

@ -32,7 +32,7 @@ ng_package(
],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = ["//integration:__pkg__"],
deps = [
":elements",
],

View File

@ -28,6 +28,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/compiler-cli/test/diagnostics:__pkg__",
"//packages/language-service/test:__pkg__",

View File

@ -34,6 +34,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
],
deps = [

View File

@ -28,7 +28,9 @@ pkg_npm(
],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = [
"//integration:__pkg__",
],
deps = [
":language-service",
# min bundle is not used at the moment; omit from package to speed up build

View File

@ -32,6 +32,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
],
deps = [

View File

@ -33,6 +33,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/compiler-cli/test:__pkg__",
],

View File

@ -38,6 +38,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
],
deps = [

View File

@ -28,6 +28,6 @@ ng_package(
],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = ["//integration:__pkg__"],
deps = [":platform-webworker-dynamic"],
)

View File

@ -32,7 +32,7 @@ ng_package(
],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = ["//integration:__pkg__"],
deps = [
":platform-webworker",
],

View File

@ -32,6 +32,7 @@ ng_package(
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = [
"//integration:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/compiler-cli/test:__pkg__",
"//packages/compiler-cli/test/transformers:__pkg__",

View File

@ -52,7 +52,7 @@ ng_package(
],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = ["//integration:__pkg__"],
deps = [
":service-worker",
"//packages/service-worker/config",

View File

@ -31,7 +31,7 @@ ng_package(
],
# Do not add more to this list.
# Dependencies on the full npm_package cause long re-builds.
visibility = ["//visibility:private"],
visibility = ["//integration:__pkg__"],
deps = [
":upgrade",
"//packages/upgrade/static",

View File

@ -1,4 +1,4 @@
load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm")
load("//tools:defaults.bzl", "pkg_npm")
load("//packages/zone.js:bundles.bzl", "ES2015_BUNDLES", "ES5_BUNDLES", "ES5_GLOBAL_BUNDLES")
exports_files([

View File

@ -1,16 +1,17 @@
"""Re-export of some bazel rules with repository-wide defaults."""
load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary", _pkg_npm = "pkg_npm")
load("@npm_bazel_jasmine//:index.bzl", _jasmine_node_test = "jasmine_node_test")
load("@npm_bazel_karma//:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite")
load("@npm_bazel_rollup//:index.bzl", _rollup_bundle = "rollup_bundle")
load("@npm_bazel_terser//:index.bzl", "terser_minified")
load("@npm_bazel_typescript//:index.bzl", _ts_devserver = "ts_devserver", _ts_library = "ts_library")
load("@npm_bazel_protractor//:index.bzl", _protractor_web_test_suite = "protractor_web_test_suite")
load("@npm//typescript:index.bzl", "tsc")
load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package")
load("//tools/ng_rollup_bundle:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle")
load("//tools:ng_benchmark.bzl", _ng_benchmark = "ng_benchmark")
load("@npm_bazel_rollup//:index.bzl", _rollup_bundle = "rollup_bundle")
load("@npm_bazel_terser//:index.bzl", "terser_minified")
load("@npm//typescript:index.bzl", "tsc")
_DEFAULT_TSCONFIG_TEST = "//packages:tsconfig-test"
_INTERNAL_NG_MODULE_API_EXTRACTOR = "//packages/bazel/src/api-extractor:api_extractor"
@ -166,6 +167,7 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg
deps = deps + [
"@npm//tslib",
]
visibility = kwargs.pop("visibility", None)
_ng_package(
name = name,
@ -177,17 +179,41 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg
terser_config_file = _INTERNAL_NG_PACKAGE_DEFALUT_TERSER_CONFIG_FILE,
rollup_config_tmpl = _INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP_CONFIG_TMPL,
rollup = _INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP,
visibility = visibility,
**kwargs
)
pkg_tar(
name = name + "_archive",
srcs = [":%s" % name],
extension = "tar.gz",
strip_prefix = "./%s" % name,
# should not be built unless it is a dependency of another rule
tags = ["manual"],
visibility = visibility,
)
def pkg_npm(name, substitutions = {}, **kwargs):
"""Default values for npm_package"""
"""Default values for pkg_npm"""
visibility = kwargs.pop("visibility", None)
_pkg_npm(
name = name,
substitutions = dict(substitutions, **PKG_GROUP_REPLACEMENTS),
visibility = visibility,
**kwargs
)
pkg_tar(
name = name + "_archive",
srcs = [":%s" % name],
extension = "tar.gz",
strip_prefix = "./%s" % name,
# should not be built unless it is a dependency of another rule
tags = ["manual"],
visibility = visibility,
)
def karma_web_test(bootstrap = [], deps = [], data = [], runtime_deps = [], **kwargs):
"""Default values for karma_web_test"""
if not bootstrap:

View File

@ -0,0 +1,3 @@
package(default_visibility = ["//visibility:public"])
exports_files(["test_runner.js"])

View File

@ -0,0 +1,198 @@
"""Npm integration testing
"""
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test")
# Returns the manifest path of a file: `workspace/path/to/file`
def _to_manifest_path(ctx, file):
if file.short_path.startswith("../"):
# Strip the ../ from short_path to external repository
return file.short_path[3:]
else:
# Add the repository name for short_path to local repository
return ctx.workspace_name + "/" + file.short_path
def _npm_integration_test_config_impl(ctx):
if len(ctx.files.test_files) == 0:
fail("No files were found to run under integration testing.")
if ctx.attr.debug:
for f in ctx.files.test_files:
if f.is_directory:
fail("In debug mode, directory test_files labels not supported.")
commands = []
for c in ctx.attr.commands:
commands.append(ctx.expand_location(c, targets = ctx.attr.data))
# pass --define vars to test; these are added to the environment using process.env().
env_vars = {}
for k in ctx.attr.configuration_env_vars:
if k in ctx.var.keys():
env_vars[k] = ctx.var[k]
# Serialize configuration file for test runner
ctx.actions.write(
output = ctx.outputs.config,
content = """// npm_integration_test runner config generated by npm_integration_test rule
module.exports = {{
testFiles: [ {TMPL_test_files} ],
commands: [ {TMPL_commands} ],
npmPackages: {{ {TMPL_npm_packages} }},
checkNpmPackages: [ {TMPL_check_npm_packages} ],
envVars: {{ {TMPL_env_vars} }},
debug: {TMPL_debug},
}};
""".format(
TMPL_test_files = ", ".join(["'%s'" % f.short_path for f in ctx.files.test_files]),
TMPL_commands = ", ".join(["'%s'" % s for s in commands]),
TMPL_npm_packages = ", ".join(["'%s': '%s'" % (ctx.attr.npm_packages[n], n.files.to_list()[0].short_path) for n in ctx.attr.npm_packages]),
TMPL_check_npm_packages = ", ".join(["'%s'" % s for s in ctx.attr.check_npm_packages]),
TMPL_env_vars = ", ".join(["'%s': '%s'" % (k, env_vars[k]) for k in env_vars]),
TMPL_debug = "true" if ctx.attr.debug else "false",
),
)
runfiles = [ctx.outputs.config] + ctx.files.test_files + ctx.files.npm_packages
return [DefaultInfo(runfiles = ctx.runfiles(files = runfiles))]
_NPM_INTEGRATION_TEST_CONFIG_ATTRS = {
"commands": attr.string_list(
default = [],
mandatory = True,
doc = """The list of test commands to run. Defaults to `[]`.""",
),
"configuration_env_vars": attr.string_list(
doc = """Pass these configuration environment variables to the resulting test.
Chooses a subset of the configuration environment variables (taken from `ctx.var`), which also
includes anything specified via the --define flag.
Note, this can lead to different results for the test.""",
default = [],
),
"check_npm_packages": attr.string_list(
doc = """A list of npm packages that should be replaced in this test.
This attribute checks that none of the npm packages lists is found in the workspace-under-test's
package.json file unlinked to a generated npm package.
This can be used to verify that all npm package artifacts that need to be tested against are indeed
replaced in all integration tests. For example,
```
check_npm_packages = [
"@angular/common",
"@angular/compiler",
"@angular/compiler-cli",
"@angular/core",
],
```
If an `npm_packages` replacement on any package listed is missed then the test will fail. Since listing all
npm packages in `npm_packages` is expensive as any change will result in all integration tests re-running,
this attribute allows a fine grained `npm_packages` per integration test with the added safety that none
are missed for any one test.
""",
),
"data": attr.label_list(
doc = """Data dependencies for test.""",
allow_files = True,
),
"debug": attr.bool(
doc = """Setup the test for debugging.
If set to true then the package.json replacement are done in-place instead of a tmp folder
and the test is not run. This is used to configure the test folder for local testing and debugging.
""",
default = False,
),
"npm_packages": attr.label_keyed_string_dict(
doc = """A label keyed string dictionary of npm package replacements to make in the workspace-under-test's
package.json with npm package targets. The targets should be pkg_tar tar.gz archives.
For example,
```
npm_packages = {
"//packages/common:npm_package_archive": "@angular/common",
"//packages/compiler:npm_package_archive": "@angular/compiler",
"//packages/compiler-cli:npm_package_archive": "@angular/compiler-cli",
"//packages/core:npm_package_archive": "@angular/core",
}
```""",
allow_files = True,
),
"test_files": attr.label(
doc = """A filegroup of all files necessary to run the test.""",
allow_files = True,
),
}
_npm_integration_test_config = rule(
implementation = _npm_integration_test_config_impl,
doc = """Generates an npm_integration_test config.""",
attrs = _NPM_INTEGRATION_TEST_CONFIG_ATTRS,
outputs = {
"config": "%{name}.js",
},
)
def npm_integration_test(name, **kwargs):
"""Runs an npm integration test.
See _NPM_INTEGRATION_TEST_CONFIG_ATTRS above for configuration arguments.
"""
commands = kwargs.pop("commands", [])
configuration_env_vars = kwargs.pop("configuration_env_vars", [])
check_npm_packages = kwargs.pop("check_npm_packages", [])
npm_packages = kwargs.pop("npm_packages", {})
test_files = kwargs.pop("test_files", [])
data = kwargs.pop("data", [])
_npm_integration_test_config(
name = name + ".config",
commands = commands,
configuration_env_vars = configuration_env_vars,
check_npm_packages = check_npm_packages,
data = data,
npm_packages = npm_packages,
test_files = test_files,
visibility = ["//visibility:private"],
tags = ["manual"],
testonly = True,
)
# Config for debug target below
_npm_integration_test_config(
name = name + ".debug.config",
commands = commands,
configuration_env_vars = configuration_env_vars,
check_npm_packages = check_npm_packages,
data = data,
npm_packages = npm_packages,
test_files = test_files,
debug = True,
visibility = ["//visibility:private"],
tags = ["manual"],
testonly = True,
)
tags = kwargs.pop("tags", [])
npm_deps = ["@npm//tmp"]
nodejs_test(
name = name,
data = data + npm_deps + [":%s.config" % name, ":%s.config.js" % name],
tags = tags,
templated_args = ["$(location :%s.config.js)" % name],
entry_point = "//tools/npm_integration_test:test_runner.js",
**kwargs
)
# Setup a .debug target that sets the debug attribute to True.
# This target must be run with `bazel run` so it is tagged manual.
nodejs_test(
name = name + ".debug",
data = data + npm_deps + [":%s.debug.config" % name, ":%s.debug.config.js" % name],
tags = tags + ["manual", "local"],
templated_args = ["$(location :%s.debug.config.js)" % name],
entry_point = "//tools/npm_integration_test:test_runner.js",
**kwargs
)

View File

@ -0,0 +1,335 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
const spawnSync = require('child_process').spawnSync;
const fs = require('fs');
const path = require('path');
const tmp = require('tmp');
const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']);
const VERBOSE_LOGS = !!process.env['VERBOSE_LOGS'];
// Set to true if you want the /tmp folder created to persist after running `bazel test`
const KEEP_TMP = false;
function fail(...m) {
console.error();
console.error(`[${path.basename(__filename)}]`);
console.error('error:', ...m);
console.error();
process.exit(1);
}
function log(...m) {
console.error(`[${path.basename(__filename)}]`, ...m);
}
function log_verbose(...m) {
if (VERBOSE_LOGS) log(...m);
}
/**
* Create a new directory and any necessary subdirectories
* if they do not exist.
*/
function mkdirp(p) {
if (!fs.existsSync(p)) {
mkdirp(path.dirname(p));
fs.mkdirSync(p);
}
}
/**
* Checks if a given path exists and is a file.
* Note: fs.statSync() is used which resolves symlinks.
*/
function isFile(p) {
return fs.existsSync(p) && fs.statSync(p).isFile();
}
/**
* Check if a given path is an executable file.
*/
function isExecutable(p) {
try {
fs.accessSync(p, fs.constants.X_OK);
return true;
} catch {
return false;
}
}
/**
* Given two arrays returns the longest common slice.
*/
function commonSlice(a, b) {
const p = a.length < b.length ? [a, b] : [b, a];
for (let i = 0; i < p[0].length; ++i) {
if (p[0][i] !== p[1][i]) {
return p[0].slice(0, i);
}
}
return p[0];
}
/**
* Given a list of files, the root directory is returned
*/
function rootDirectory(files) {
let root = path.dirname(files[0]).replace(/\\/g, '/').split('/');
for (f of files) {
root = commonSlice(root, path.dirname(f).replace(/\\/g, '/').split('/'));
if (!root.length) break;
}
if (!root.length) {
fail(`not all test files are under the same root!`);
}
return root.join('/');
}
/**
* Utility function to copy a list of files under a common root to a destination folder.
*/
function copy(files, root, to) {
for (src of files) {
if (!src.startsWith(root)) {
fail(`file to copy ${src} is not under root ${root}`);
}
if (isFile(src)) {
const rel = src.slice(root.length + 1);
if (rel.startsWith('node_modules/')) {
// don't copy nested node_modules
continue;
}
const dest = `${to}/${rel}`;
mkdirp(path.dirname(dest));
fs.copyFileSync(src, dest);
// Set file permissions to set for files copied to tmp folders.
// These are needed as files copied out of bazel-bin will have
// restrictive permissions that may break tests.
fs.chmodSync(dest, isExecutable(src) ? '755' : '644');
log_verbose(`copied file ${src} -> ${dest}`);
} else {
fail('directories in test_files not supported');
}
}
return to;
}
/**
* Utility function to copy a list of files to a tmp folder based on their common root.
*/
function copyToTmp(files) {
const resolved = files.map(f => runfiles.resolveWorkspaceRelative(f));
return copy(
resolved, rootDirectory(resolved),
tmp.dirSync({keep: KEEP_TMP, unsafeCleanup: !KEEP_TMP}).name);
}
/**
* TestRunner handles setting up the integration test and executing
* the test commands based on the config.
*/
class TestRunner {
constructor(config) {
this.config = config;
this.successful = 0;
this._setupTestFiles();
}
/**
* Run all test commands in the integration test.
* Returns on first failure.
*/
run() {
for (const command of this.config.commands) {
// TODO: handle a quoted binary path that contains a space such as "/path to/binary"
// and quoted arguments that contain spaces
const split = command.split(' ');
let binary = split[0];
const args = split.slice(1);
switch (binary) {
case 'patch-package-json': {
let packageJsonFile = 'package.json';
if (args.length > 0) {
packageJsonFile = args[0];
}
log(`running test command ${this.successful+1} of ${this.config.commands.length}: patching '${packageJsonFile}' in '${this.testRoot}'`);
this._patchPackageJson(packageJsonFile);
} break;
default: {
if (binary.startsWith('external/')) {
binary = `../${binary.substring('external/'.length)}`;
}
const runfilesBinary = runfiles.resolveWorkspaceRelative(binary);
binary = fs.existsSync(runfilesBinary) ? runfilesBinary : binary;
log(`running test command ${this.successful+1} of ${this.config.commands.length}: '${binary} ${args.join(' ')}' in '${this.testRoot}'`);
const spawnedProcess = spawnSync(binary, args, {cwd: this.testRoot, stdio: 'inherit'});
if (spawnedProcess.error) {
fail(
`test command ${testRunner.successful+1} '${binary} ${args.join(' ')}' failed with ${spawnedProcess.error.code}`);
}
if (spawnedProcess.status) {
log(`test command ${testRunner.successful+1} '${binary} ${args.join(' ')}' failed with status code ${spawnedProcess.status}`);
return spawnedProcess.status;
}
}
}
this.successful++;
}
return 0;
}
/**
* @internal
*
* Patch the specified package.json file with the npmPackages passed in the config.
*/
_patchPackageJson(packageJsonFile) {
const packageJson = `${this.testRoot}/${packageJsonFile}`;
if (!isFile(packageJson)) {
fail(`no ${packageJsonFile} file found at test root ${this.testRoot}`);
}
const contents = JSON.parse(fs.readFileSync(packageJson, {encoding: 'utf-8'}));
let replacements = 0;
// replace npm packages
for (const key of Object.keys(this.config.npmPackages)) {
const path = runfiles.resolveWorkspaceRelative(this.config.npmPackages[key]);
const replacement = `file:${path}`;
if (contents.dependencies && contents.dependencies[key]) {
replacements++;
contents.dependencies[key] = replacement;
log(`overriding dependencies['${key}'] npm package with 'file:${path}' in package.json file`);
}
if (contents.devDependencies && contents.devDependencies[key]) {
replacements++;
contents.devDependencies[key] = replacement;
log(`overriding devDependencies['${key}'] npm package with 'file:${path}' in package.json file`);
}
if (contents.resolutions && contents.resolutions[key]) {
replacements++;
contents.resolutions[key] = replacement;
log(`overriding resolutions['${key}'] npm package with 'file:${path}' in package.json file`);
}
// TODO: handle other formats for resolutions such as `some-package/${key}` or
// `some-package/**/${key}`
const altKey = `**/${key}`;
if (contents.resolutions && contents.resolutions[altKey]) {
replacements++;
contents.resolutions[altKey] = replacement;
log(`overriding resolutions['${altKey}'] npm package with 'file:${path}' in package.json file`);
}
}
// check packages that must be replaced
const failedPackages = [];
for (const key of this.config.checkNpmPackages) {
if (contents.dependencies && contents.dependencies[key] &&
(!contents.dependencies[key].startsWith('file:') ||
contents.dependencies[key].startsWith('file:.'))) {
failedPackages.push(key);
} else if (
contents.devDependencies && contents.devDependencies[key] &&
(!contents.devDependencies[key].startsWith('file:') ||
contents.devDependencies[key].startsWith('file:.'))) {
failedPackages.push(key);
}
}
const contentsEncoded = JSON.stringify(contents, null, 2);
log(`package.json file:\n${contentsEncoded}`);
if (failedPackages.length) {
fail(
`expected replacements of npm packages ${JSON.stringify(failedPackages)} not found; add these to the npm_packages attribute`);
}
if (replacements) {
fs.writeFileSync(packageJson, contentsEncoded);
}
}
/**
* @internal
*
* Copy all the test files to a tmp folder so they are sandboxed for the test
* and so that changes made to source files such as patching package.json
* do not persist in the user's workspace.
*
* In debug mode, do not copy any file but set the testRoot to the user's workspace.
*/
_setupTestFiles() {
if (!this.config.testFiles.length) {
fail(`no test files`);
}
if (this.config.debug) {
// Setup the test in the test files root directory
const root = rootDirectory(this.config.testFiles);
if (path.isAbsolute(root)) {
fail(`root directory of Bazel test files should not be an absolute path but got '${root}'`);
}
if (root.startsWith(`../`)) {
fail(`debug mode only available with test files in the root workspace`);
}
let workspaceDirectory = process.env['BUILD_WORKSPACE_DIRECTORY'];
if (!workspaceDirectory) {
// bazel test
const runfilesPath = process.env['RUNFILES'];
if (!runfilesPath) {
fail(`RUNFILES environment variable is not set`);
}
// read the contents of the output_base/DO_NOT_BUILD_HERE file to get
// the workspace directory
const index = runfilesPath.search(/[\\/]execroot[\\/]/);
if (index === -1) {
fail(`no /execroot/ in runfiles path`);
}
const outputBase = runfilesPath.substr(0, index);
workspaceDirectory =
fs.readFileSync(`${outputBase}/DO_NOT_BUILD_HERE`, {encoding: 'utf-8'});
}
this.testRoot = `${workspaceDirectory}/${root}`;
log(`configuring test in-place under ${this.testRoot}`);
} else {
this.testRoot = copyToTmp(this.config.testFiles);
log(`test files from '${rootDirectory(this.config.testFiles)}' copied to tmp folder ${this.testRoot}`);
}
}
}
const config = require(process.argv[2]);
// set env vars passed from --define
for (const k of Object.keys(config.envVars)) {
const v = config.envVars[k];
process.env[k] = v;
log_verbose(`set environment variable ${k}='${v}'`);
}
log_verbose(`env: ${JSON.stringify(process.env, null, 2)}`);
log_verbose(`config: ${JSON.stringify(config, null, 2)}`);
log(`running in ${process.cwd()}`);
const testRunner = new TestRunner(config);
const result = testRunner.run();
log(`${testRunner.successful} of ${config.commands.length} test commands successful`);
if (result) {
if (!config.debug) {
log(`to run this integration test in debug mode:
bazel run ${process.env['TEST_TARGET']}.debug
in debug mode the integration test will be run out of the workspace folder`);
}
}
if (config.debug) {
log(`this integration test may be re-run manually from the '${testRunner.testRoot}' folder
for example,
cd ${testRunner.testRoot}
yarn test`);
}
process.exit(result);

View File

@ -13,6 +13,8 @@ const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']);
// path of a tree because we want to determine the path to the directory that includes all
// test fixture runfiles. On Windows this is usually the original non-sandboxed disk location,
// otherwise this just refers to the runfile directory with all the proper symlinked files.
// NB: we resolve `test/fixtures/empty.ts` and then step up 3 folders so to ensure we resolve to the
// root of the source tree and not the output tree on Windows where there are no runfiles.
// TODO: remove the whole bootstrap file once the tests are Bazel and Windows compatible.
process.chdir(path.dirname(path.dirname(
path.dirname(runfiles.resolve('angular/tools/ts-api-guardian/test/fixtures/empty.ts')))));
process.chdir(path.resolve(
runfiles.resolve('angular/tools/ts-api-guardian/test/fixtures/empty.ts'), '../../..'));

View File

@ -3288,9 +3288,9 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001022:
integrity sha512-FjwPPtt/I07KyLPkBQ0g7/XuZg6oUkYBVnPHNj3VHJbOjmmJ/GdSo/GUY6MwINEQvjhP6WZVbX8Tvms8xh0D5A==
caniuse-lite@^1.0.30001006, caniuse-lite@^1.0.30001010:
version "1.0.30001011"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001011.tgz#0d6c4549c78c4a800bb043a83ca0cbe0aee6c6e1"
integrity sha512-h+Eqyn/YA6o6ZTqpS86PyRmNWOs1r54EBDcd2NTwwfsXQ8re1B38SnB+p2RKF8OUsyEIjeDU8XGec1RGO/wYCg==
version "1.0.30001012"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001012.tgz#653ec635e815b9e0fb801890923b0c2079eb34ec"
integrity sha512-7RR4Uh04t9K1uYRWzOJmzplgEOAXbfK72oVNokCdMzA67trrhPzy93ahKk1AWHiA0c58tD2P+NHqxrA8FZ+Trg==
caniuse-lite@^1.0.30001017:
version "1.0.30001021"
@ -5142,9 +5142,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.306:
version "1.3.311"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.311.tgz#73baa361e2b1f44b7b4f1a443aaa1372f8074ebb"
integrity sha512-7GH6RKCzziLzJ9ejmbiBEdzHZsc6C3eRpav14dmRfTWMpNgMqpP1ukw/FU/Le2fR+ep642naq7a23xNdmh2s+A==
version "1.3.314"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.314.tgz#c186a499ed2c9057bce9eb8dca294d6d5450facc"
integrity sha512-IKDR/xCxKFhPts7h+VaSXS02Z1mznP3fli1BbXWXeN89i2gCzKraU8qLpEid8YzKcmZdZD3Mly3cn5/lY9xsBQ==
electron-to-chromium@^1.3.322:
version "1.3.334"