blob: 855d85938e831fb707bfbf16c44fc47f580bf5c5 [file]
"""A test rule for plain JavaScript, using Node's built-in test runner.
Gitiles already depends on `rules_nodejs`, whose module registers a hermetic
Node toolchain for every supported platform, so this rule needs no new
dependency and no `MODULE.bazel` change. `rules_nodejs` 6.x ships toolchains
only -- `nodejs_test` lives in `aspect_rules_js` -- hence the rule here.
Tests are written against `node:test` and `node:assert`, which are part of the
runtime. There is no package manager, no lockfile and no `node_modules`.
"""
# Bazel passes XML_OUTPUT_FILE when it wants a JUnit report; without it a test
# failure is just a red target with a log to go read. "$@" carries the `args`
# attribute and --test_arg, e.g. --test_arg=--test-name-pattern=...
_LAUNCHER = """#!/bin/sh
set -eu
if [ -n "${{XML_OUTPUT_FILE:-}}" ]; then
exec ./{node} --test --test-reporter=junit \\
--test-reporter-destination="$XML_OUTPUT_FILE" \\
--test-reporter=spec --test-reporter-destination=stdout "$@" {tests}
fi
exec ./{node} --test "$@" {tests}
"""
def _js_test_impl(ctx):
# The runtime toolchain is selected for the target platform, where the
# test runs; toolchain_type is selected for the exec platform of actions.
node = ctx.toolchains["@rules_nodejs//nodejs:runtime_toolchain_type"].nodeinfo.node
launcher = ctx.actions.declare_file(ctx.label.name + ".sh")
ctx.actions.write(
output = launcher,
is_executable = True,
content = _LAUNCHER.format(
node = node.short_path,
tests = " ".join([f.short_path for f in ctx.files.srcs]),
),
)
runfiles = ctx.runfiles(files = ctx.files.srcs + ctx.files.data + [node])
runfiles = runfiles.merge_all([
dep[DefaultInfo].default_runfiles
for dep in ctx.attr.srcs + ctx.attr.data
])
return [DefaultInfo(executable = launcher, runfiles = runfiles)]
js_test = rule(
doc = "Runs `node --test` over `srcs` with `data` available in runfiles.",
implementation = _js_test_impl,
test = True,
attrs = {
"srcs": attr.label_list(
doc = "Test files, passed to `node --test`.",
allow_files = [".js"],
allow_empty = False,
mandatory = True,
),
"data": attr.label_list(
doc = "Files the tests read at run time, such as the code under test.",
allow_files = True,
),
},
toolchains = ["@rules_nodejs//nodejs:runtime_toolchain_type"],
)