Skip to content

Commit 77e5c21

Browse files
authored
build: run unit tests with partial compilation and Angular linker (#22979)
Instead of running e2e tests against a linker-processed application, we run all of our unit tests using partial compilation and the Angular linker. This ensures we test our components more throroughly against the Angular linker & partial compilation. This also solves our CI flakiness issue due to the large amount of webdriver e2e tests running concurrently on CI (especially since all e2e tests ran on snapshot builds).
1 parent 81cd26b commit 77e5c21

File tree

10 files changed

+297
-143
lines changed

10 files changed

+297
-143
lines changed

.circleci/config.yml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,7 @@ jobs:
197197

198198
# Exclude release and docs packages here as those will be built within
199199
# the "build_release_packages" and "publish_snapshots" jobs.
200-
# TODO(devversion): remove target exclusion once https://github.com/bazelbuild/rules_nodejs/pull/2646 is available.
201-
- run: bazel build --build_tag_filters=-docs-package,-release-package -- src/... -//src/e2e-app:devserver_with_linked_declarations.MF
200+
- run: bazel build --build_tag_filters=-docs-package,-release-package -- src/...
202201
- *slack_notify_on_failure
203202

204203
# -----------------------------------
@@ -220,8 +219,7 @@ jobs:
220219

221220
# Exclude release and docs packages here as those will be built within
222221
# the "build_release_packages" and "publish_snapshots" jobs.
223-
# TODO(devversion): remove target exclusion once https://github.com/bazelbuild/rules_nodejs/pull/2646 is available.
224-
- run: bazel build --build_tag_filters=-docs-package,-release-package --config=view-engine -- src/... -//src/e2e-app:devserver_with_linked_declarations.MF
222+
- run: bazel build --build_tag_filters=-docs-package,-release-package --config=view-engine -- src/...
225223
- *slack_notify_on_failure
226224

227225
# --------------------------------------------------------------------------------------------
@@ -283,8 +281,7 @@ jobs:
283281
- *yarn_install
284282
- *setup_bazel_binary
285283

286-
# TODO(devversion): remove target exclusion once https://github.com/bazelbuild/rules_nodejs/pull/2646 is available.
287-
- run: bazel test --build_tag_filters=-e2e --test_tag_filters=-e2e --build_tests_only -- src/... -//src/e2e-app:devserver_with_linked_declarations.MF
284+
- run: bazel test --build_tag_filters=-e2e --test_tag_filters=-e2e --build_tests_only -- src/...
288285
- *slack_notify_on_failure
289286

290287
# ----------------------------------------------------------------------------
@@ -536,8 +533,7 @@ jobs:
536533
- *yarn_install_loose_lockfile
537534
- *setup_bazel_binary
538535

539-
# TODO(devversion): remove target exclusion once https://github.com/bazelbuild/rules_nodejs/pull/2646 is available.
540-
- run: bazel test --build_tag_filters=-e2e --test_tag_filters=-e2e --build_tests_only -- src/... -//src/e2e-app:devserver_with_linked_declarations.MF
536+
- run: bazel test --build_tag_filters=-e2e --test_tag_filters=-e2e --build_tests_only -- src/...
541537
- *slack_notify_on_failure
542538

543539
# ----------------------------------------------------------------------------
@@ -558,8 +554,7 @@ jobs:
558554
- *setup_bazel_binary
559555

560556
# Run project tests with NGC and View Engine.
561-
# TODO(devversion): remove target exclusion once https://github.com/bazelbuild/rules_nodejs/pull/2646 is available.
562-
- run: bazel test --build_tag_filters=-docs-package,-e2e --test_tag_filters=-e2e --config=view-engine --build_tests_only -- src/... -//src/e2e-app:devserver_with_linked_declarations.MF
557+
- run: bazel test --build_tag_filters=-docs-package,-e2e --test_tag_filters=-e2e --config=view-engine --build_tests_only -- src/...
563558
- *slack_notify_on_failure
564559

565560
# ----------------------------------------------------------------------------
@@ -580,8 +575,7 @@ jobs:
580575
- *setup_bazel_binary
581576

582577
# Run project tests with NGC and View Engine.
583-
# TODO(devversion): remove target exclusion once https://github.com/bazelbuild/rules_nodejs/pull/2646 is available.
584-
- run: bazel test --build_tag_filters=-docs-package,-e2e --test_tag_filters=-e2e --config=view-engine --build_tests_only -- src/... -//src/e2e-app:devserver_with_linked_declarations.MF
578+
- run: bazel test --build_tag_filters=-docs-package,-e2e --test_tag_filters=-e2e --config=view-engine --build_tests_only -- src/...
585579
- *slack_notify_on_failure
586580

587581
# ----------------------------------------------------------------------------
@@ -654,8 +648,7 @@ jobs:
654648

655649
# Setup the components repository to use the MDC snapshot builds.
656650
# Run project tests with the MDC canary builds.
657-
# TODO(devversion): remove target exclusion once https://github.com/bazelbuild/rules_nodejs/pull/2646 is available.
658-
- run: bazel test --build_tag_filters=-docs-package,-e2e --test_tag_filters=-e2e --build_tests_only -- src/... -//src/e2e-app:devserver_with_linked_declarations.MF
651+
- run: bazel test --build_tag_filters=-docs-package,-e2e --test_tag_filters=-e2e --build_tests_only -- src/...
659652
- *slack_notify_on_failure
660653

661654
# ----------------------------------------------------------------------------------------

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"approve-size-tests": "node ./scripts/approve-size-golden.js",
4646
"integration-tests": "bazel test --test_tag_filters=-view-engine-only,-linker-integration-test --build_tests_only -- //integration/... -//integration/size-test/...",
4747
"integration-tests:view-engine": "bazel test --test_tag_filters=view-engine-only --build_tests_only -- //integration/... -//integration/size-test/...",
48-
"integration-tests:partial-ivy": "bazel test --//tools:partial_compilation=True --test_tag_filters=partial-compilation-integration --build_tests_only -- //integration/... //src/...",
48+
"integration-tests:partial-ivy": "bazel test --//tools:partial_compilation=True --test_tag_filters=partial-compilation-integration,-firefox --build_tests_only -- //integration/... //src/...",
4949
"integration-tests:size-test": "bazel test //integration/size-test/...",
5050
"check-mdc-tests": "ts-node --project scripts/tsconfig.json scripts/check-mdc-tests.ts",
5151
"check-mdc-exports": "ts-node --project scripts/tsconfig.json scripts/check-mdc-exports.ts",

src/e2e-app/BUILD.bazel

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
21
load("@npm//@bazel/concatjs:index.bzl", "concatjs_devserver")
32
load("//:packages.bzl", "getAngularUmdTargets")
43
load("//tools:defaults.bzl", "ng_module", "sass_binary")
@@ -117,45 +116,3 @@ concatjs_devserver(
117116
tags = ["manual"],
118117
deps = [":e2e-app"],
119118
)
120-
121-
# Bundles the `e2e-app` using rollup while running the Angular linker to transform
122-
# partial declarations to their corresponding Angular definitions. This target should
123-
# only be run if the e2e-app is built in partial compilation mode. Technically this
124-
# target could be run with full compilation mode too, but we want to raise an error if
125-
# the partial compilation is not enabled (as the mode has been forgotten most likely).
126-
rollup_bundle(
127-
name = "linked_app_bundle",
128-
testonly = True,
129-
config_file = ":rollup-with-linker.config.js",
130-
entry_points = {
131-
"main.ts": "bundle",
132-
},
133-
sourcemap = "false",
134-
tags = ["manual"],
135-
deps = select(
136-
{
137-
# We rely on the partial compilation build setting here to ensure that the target
138-
# cannot be built if partial compilation is not enabled.
139-
"//tools:partial_compilation_enabled": [
140-
":e2e-app",
141-
"@npm//rollup-plugin-node-resolve",
142-
"@npm//@angular/compiler-cli",
143-
"@npm//@rollup/plugin-babel",
144-
"@npm//@rollup/plugin-commonjs",
145-
"@npm//glob",
146-
],
147-
},
148-
no_match_error = "Partial compilation needs to be enabled. The following flag enables partial " +
149-
"compilation: --//tools:partial_compilation=True",
150-
),
151-
)
152-
153-
# Basic web server that serves the e2e-app processed by the Angular linker.
154-
concatjs_devserver(
155-
name = "devserver_with_linked_declarations",
156-
testonly = True,
157-
additional_root_paths = ["npm/node_modules"],
158-
port = 4200,
159-
static_files = [":linked_app_bundle"] + devserverIndexHtmlDependencies,
160-
tags = ["manual"],
161-
)

src/e2e-app/rollup-with-linker.config.js

Lines changed: 0 additions & 76 deletions
This file was deleted.

src/e2e-app/test_suite.bzl

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
load("//tools:defaults.bzl", "protractor_web_test_suite")
22

3-
def e2e_test_suite(name, data = [], tags = ["e2e", "partial-compilation-integration"], deps = []):
3+
def e2e_test_suite(name, data = [], tags = ["e2e"], deps = []):
44
protractor_web_test_suite(
55
name = name,
66
configuration = "//src/e2e-app:protractor.conf.js",
@@ -9,12 +9,7 @@ def e2e_test_suite(name, data = [], tags = ["e2e", "partial-compilation-integrat
99
"@npm//@axe-core/webdriverjs",
1010
] + data,
1111
on_prepare = "//src/e2e-app:start-devserver.js",
12-
# Based on whether the partial compilation mode is enabled, test either with the default e2e-app
13-
# server, or test with a server that processed all sources with the Angular linker.
14-
server = select({
15-
"//conditions:default": "//src/e2e-app:devserver",
16-
"//tools:partial_compilation_enabled": "//src/e2e-app:devserver_with_linked_declarations",
17-
}),
12+
server = "//src/e2e-app:devserver",
1813
tags = tags,
1914
deps = deps,
2015
)

tools/defaults.bzl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ load("@npm//@bazel/typescript:index.bzl", _ts_library = "ts_library")
99
load("//:packages.bzl", "VERSION_PLACEHOLDER_REPLACEMENTS", "getAngularUmdTargets")
1010
load("//:rollup-globals.bzl", "ROLLUP_GLOBALS")
1111
load("//tools/markdown-to-html:index.bzl", _markdown_to_html = "markdown_to_html")
12+
load("//tools/linker-process:index.bzl", "linker_process")
1213

1314
_DEFAULT_TSCONFIG_BUILD = "//src:bazel-tsconfig-build.json"
1415
_DEFAULT_TSCONFIG_TEST = "//src:tsconfig-test"
@@ -161,8 +162,22 @@ def ng_e2e_test_library(deps = [], tsconfig = None, **kwargs):
161162

162163
def karma_web_test_suite(name, **kwargs):
163164
web_test_args = {}
165+
test_deps = ["//tools/rxjs:rxjs_umd_modules"] + kwargs.get("deps", [])
166+
164167
kwargs["srcs"] = ["@npm//:node_modules/tslib/tslib.js"] + getAngularUmdTargets() + kwargs.get("srcs", [])
165-
kwargs["deps"] = ["//tools/rxjs:rxjs_umd_modules"] + kwargs.get("deps", [])
168+
kwargs["tags"] = ["partial-compilation-integration"] + kwargs.get("tags", [])
169+
kwargs["deps"] = select({
170+
# Based on whether partial compilation is enabled, use the linker processed dependencies.
171+
"//tools:partial_compilation_enabled": ["%s_linker_processed_deps" % name],
172+
"//conditions:default": test_deps,
173+
})
174+
175+
linker_process(
176+
name = "%s_linker_processed_deps" % name,
177+
srcs = test_deps,
178+
testonly = True,
179+
tags = ["manual"],
180+
)
166181

167182
# Set up default browsers if no explicit `browsers` have been specified.
168183
if not hasattr(kwargs, "browsers"):

tools/linker-process/BUILD.bazel

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
2+
load("//tools:defaults.bzl", "ts_library")
3+
4+
package(default_visibility = ["//visibility:public"])
5+
6+
ts_library(
7+
name = "sources",
8+
srcs = glob(["**/*.ts"]),
9+
deps = [
10+
"@npm//@angular/compiler-cli",
11+
"@npm//@babel/core",
12+
"@npm//@babel/traverse",
13+
"@npm//@types/node",
14+
],
15+
)
16+
17+
# Exposes the `linker-process` tool as executable so that it can be used as
18+
# build tool (for `ctx.actions.run`) within the `linker_process` custom Bazel rule.
19+
nodejs_binary(
20+
name = "linker-process",
21+
data = [
22+
":sources",
23+
],
24+
entry_point = ":linker-process.ts",
25+
templated_args = [
26+
# TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver
27+
# See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324
28+
"--bazel_patch_module_resolver",
29+
],
30+
)

tools/linker-process/index.bzl

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
Exposes a custom Bazel rule for processing sources, which are extracted from Bazel
3+
targets, with the Angular linker plugin.
4+
"""
5+
6+
load("@build_bazel_rules_nodejs//:providers.bzl", "JSModuleInfo", "JSNamedModuleInfo")
7+
8+
"""
9+
Gets the Bazel manifest path for a given file. Manifest paths are used within Bazel runfile
10+
manifests and are formatted as followed: `<workspace_name>/<workspace_relative_file_path>`
11+
"""
12+
13+
def _to_manifest_path(ctx, file):
14+
# If a file resides outside of the current workspace, we omit the leading `../`
15+
# segment as the rest will contain the workspace name. e.g. `../npm/node_modules/<..>`.
16+
if file.short_path.startswith("../"):
17+
return file.short_path[3:]
18+
else:
19+
return ctx.workspace_name + "/" + file.short_path
20+
21+
"""Extracts all source files from the specified list of dependencies."""
22+
23+
def _extract_source_files(deps):
24+
depsets = []
25+
for dep in deps:
26+
if JSNamedModuleInfo in dep:
27+
depsets.append(dep[JSNamedModuleInfo].sources)
28+
elif JSModuleInfo in dep:
29+
depsets.append(dep[JSModuleInfo].sources)
30+
elif hasattr(dep, "files"):
31+
depsets.append(dep.files)
32+
return depset(transitive = depsets)
33+
34+
def _linker_process(ctx):
35+
args = ctx.actions.args()
36+
sources = _extract_source_files(ctx.attr.srcs)
37+
tmp_dir_name = ctx.label.name
38+
39+
# The output directory manifest path. e.g `angular_material/src/cdk/a11y/linker_processed`.
40+
output_dir_manifest_path = "%s/%s/%s" % (ctx.workspace_name, ctx.label.package, tmp_dir_name)
41+
42+
# The output directory exec path. e.g `bazel_out/<..>/src/cdk/a11y/linker_processed`.
43+
output_dir_exec_path = "%s/%s/%s" % (ctx.bin_dir.path, ctx.label.package, tmp_dir_name)
44+
45+
# Given the sources being transformed and written to a new location, the AMD module names
46+
# need to be rewritten. This file maps AMD modules as per the new location to the AMD modules
47+
# as they appear in the sources. i.e. we generate AMD module aliases.
48+
amd_module_mapping_file = ctx.actions.declare_file("%s/_module_mappings.js" % tmp_dir_name)
49+
50+
args.add(output_dir_exec_path)
51+
args.add(output_dir_manifest_path)
52+
args.add(amd_module_mapping_file.path)
53+
54+
outputs = [amd_module_mapping_file]
55+
56+
# Iterate through the determined sources and pass them to the tool as argument.
57+
for input_file in sources.to_list():
58+
output_pkg_path = _to_manifest_path(ctx, input_file)
59+
args.add("%s:%s" % (input_file.path, output_pkg_path))
60+
outputs.append(ctx.actions.declare_file("%s/%s" % (tmp_dir_name, output_pkg_path)))
61+
62+
# Support passing arguments through a parameter file. This is necessary because on Windows
63+
# there is an argument limit and we need to handle a large amount of input files. Bazel
64+
# switches between parameter file and normal argument passing based on the operating system.
65+
# Read more here: https://docs.bazel.build/versions/master/skylark/lib/Args.html#use_param_file
66+
args.use_param_file(param_file_arg = "--param-file=%s", use_always = True)
67+
68+
ctx.actions.run(
69+
inputs = sources,
70+
outputs = outputs,
71+
executable = ctx.executable._linker_process_tool,
72+
arguments = [args],
73+
progress_message = "NgLinkerProcess",
74+
)
75+
76+
outputs_depset = depset(outputs)
77+
78+
return [
79+
DefaultInfo(files = outputs_depset),
80+
]
81+
82+
"""
83+
Rule definition for the "linker_process" rule that can process a list of targets
84+
with the Angular linker. The processed files can be retrieved through the default
85+
files provider, or through the `JSNamedModuleInfo` provider.
86+
"""
87+
linker_process = rule(
88+
implementation = _linker_process,
89+
attrs = {
90+
"srcs": attr.label_list(
91+
allow_files = True,
92+
doc = """List of sources that should be processed with the Angular linker.""",
93+
),
94+
"_linker_process_tool": attr.label(
95+
default = Label("//tools/linker-process"),
96+
executable = True,
97+
cfg = "host",
98+
),
99+
},
100+
)

0 commit comments

Comments
 (0)