Skip to content

Commit

Permalink
docs(builtin): document userspace workaround for running actions/test…
Browse files Browse the repository at this point in the history
…s in a specific working directory

Fixes #1840
  • Loading branch information
Alex Eagle authored and alexeagle committed Jul 5, 2020
1 parent d660ca1 commit 245f0f8
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 2 deletions.
11 changes: 9 additions & 2 deletions internal/node/node.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,15 @@ your own test runner. For example, the `ts-api-guardian` library has a way to
assert the public API of a TypeScript program, and uses `nodejs_test` here:
https://github.com/angular/angular/blob/master/tools/ts-api-guardian/index.bzl
If you just want to run a standard test using a test runner like Karma or Jasmine,
use the specific rules for those test runners, e.g. `jasmine_node_test`.
If you just want to run a standard test using a test runner from npm, use the generated
*_test target created by npm_install/yarn_install, such as `mocha_test`.
Some test runners like Karma and Jasmine have custom rules with added features, e.g. `jasmine_node_test`.
Bazel always runs tests with a working directory set to your workspace root.
If your test needs to run in a different directory, you can write a `process.chdir` helper script
and invoke it before the test with a `--require` argument, like
`templated_args = ["--node_options=--require=./$(rootpath chdir.js)"]`.
See rules_nodejs/internal/node/test/chdir for an example.
To debug a Node.js test, we recommend saving a group of flags together in a "config".
Put this in your `tools/bazel.rc` so it's shared with your team:
Expand Down
6 changes: 6 additions & 0 deletions internal/node/npm_package_bin.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ def npm_package_bin(tool = None, package = None, package_bin = None, data = [],
This is like a genrule() except that it runs our launcher script that first
links the node_modules tree before running the program.
Bazel always runs actions with a working directory set to your workspace root.
If your tool needs to run in a different directory, you can write a `process.chdir` helper script
and invoke it before the action with a `--require` argument, like
`args = ["--node_options=--require=./$(execpath chdir.js)"]`
See rules_nodejs/internal/node/test/chdir for an example.
This is a great candidate to wrap with a macro, as documented:
https://docs.bazel.build/versions/master/skylark/macros.html#full-example
Expand Down
62 changes: 62 additions & 0 deletions internal/node/test/chdir/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Demonstrate and test the workaround for running build/test actions in the output directory
# Needed for tools like react-scripts and maybe vue-cli which don't allow specifying a path to
# the project when you call them.
# See https://github.com/bazelbuild/rules_nodejs/issues/1840

load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin", "nodejs_binary", "nodejs_test", "npm_package_bin")
load("//third_party/github.com/bazelbuild/bazel-skylib:rules/write_file.bzl", "write_file")

# A tool like react-scripts needs to run in the output directory since it writes outputs
# to $pwd/build
# That means it also needs to find inputs in that directory.
# So we copy all the inputs it needs.
copy_to_bin(
name = "copy_input",
srcs = ["package.json"],
)

# Here's our trick: write a process.chdir one-liner JS script
write_file(
name = "write_chdir_script",
out = "chdir.js",
# __dirname will be whatever directory the other outputs
# from this package are in, either in execroot or runfiles root
# depending on where Bazel places this file.
content = ["process.chdir(__dirname)"],
)

# Trivial tool to mock react-scripts
nodejs_binary(
name = "tool_bin",
entry_point = "tool.js",
)

# This tool is like react-scripts and wants to run in our directory
# with our package.json, and always writes to "build/app.js
npm_package_bin(
name = "call_tool",
outs = ["build/app.js"],
# This tool produces outputs, so it is a build action and needs execpath helper
args = ["--node_options=--require=./$(execpath chdir.js)"],
# We can't reference label "package.json" here because it's ambiguous
# whether that points to the InputArtifact package.json in the src
# folder or the output. Using "copy_input" is unambiguous.
data = [
"chdir.js",
"copy_input",
],
tool = ":tool_bin",
)

nodejs_test(
name = "test",
data = [
"build/app.js",
"chdir.js",
],
entry_point = "test.js",
# Also run a test in the output directory, this needs rootpath helper
# NB: --require=./$(rootpath chdir.js) works when runfiles are symlinked
# but $$(rlocation) is needed for Windows.
templated_args = ["--node_options=--require=$$(rlocation $(rootpath chdir.js))"],
)
3 changes: 3 additions & 0 deletions internal/node/test/chdir/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "chdir_test"
}
6 changes: 6 additions & 0 deletions internal/node/test/chdir/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const {readFileSync} = require('fs');
const assert = require('assert');

// this test should be run in a working directory with
// that file in it
assert.equal('console.log("hello world")', readFileSync('build/app.js', 'utf-8'));
13 changes: 13 additions & 0 deletions internal/node/test/chdir/tool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @fileoverview Mimics the semantics of react-scripts
* Reads inputs and writes outputs relative to the working directory
*/
const fs = require('fs');

const json = JSON.parse(fs.readFileSync('package.json'));
if (json.name != 'chdir_test') {
throw new Error('read the wrong package.json')
}

if (!fs.existsSync('build')) fs.mkdirSync('build');
fs.writeFileSync('build/app.js', 'console.log("hello world")');

0 comments on commit 245f0f8

Please sign in to comment.