Skip to content

Commit 277a65c

Browse files
Zettenpcj
authored andcommitted
Add tests for polymer-cli binary (#48)
* Add tests for polymer-cli * Omit filenames with spaces from node_modules * Set node_modules dir correctly for @foo/bar modules * Collect @foo/bar modules from node_modules * Remove only circular dependencies from a cycle, not all dependencies * Add named-workspace-relative runfiles lookup to node_binary
1 parent a447bee commit 277a65c

File tree

8 files changed

+102
-30
lines changed

8 files changed

+102
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/tests/*/bazel-*

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ test_typescript:
1616
test_webpack:
1717
(cd tests/webpack && bazel test //...)
1818

19+
test_polymer-cli:
20+
(cd tests/polymer-cli && bazel test //...)
21+
1922
test_mocha:
2023
(cd tests/mocha && bazel test //...)
2124

22-
test_all: test_helloworld test_lyrics test_express test_namespace test_typescript test_webpack test_mocha
25+
test_all: test_helloworld test_lyrics test_express test_namespace test_typescript test_webpack test_polymer-cli test_mocha

node/internal/node_binary.bzl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def create_launcher(ctx, output_dir, node, manifest):
2828
# The package path is the path
2929
package_path = []
3030
if ctx.label.workspace_root:
31-
package_path.append(ctx.label.workspace_root)
31+
package_path.append(ctx.label.workspace_root.replace("external/", "", 1))
3232
if ctx.label.package:
3333
package_path.append(ctx.label.package)
3434

@@ -52,25 +52,31 @@ def create_launcher(ctx, output_dir, node, manifest):
5252
# Namespace where node and node_modules assets have been
5353
# built.
5454
'TARGET_NAME="%s"' % ctx.attr.target,
55+
# Workspace name where this rule is defined
56+
'WORKSPACE_NAME="%s"' % ctx.workspace_name,
5557
# Based on the way the script has been invoked, $PACKAGE_PATH
5658
# can exist in various locations. We need to discover this
5759
# and assign the value to $TARGET_PATH.
5860
'TARGET_PATH=""' ,
5961

6062
'ENTRYPOINT="%s"' % entrypoint,
61-
63+
6264
'if [[ -e "${0}_files/${NODE_EXE}" ]]; then',
6365
' TARGET_PATH="${0}_files/"',
6466
#' echo "Matched [standalone script] or [bazel test] context [${TARGET_PATH}]"',
6567
'',
66-
'elif [[ -e "${0}_files/${NODE_EXE}" ]]; then',
67-
' TARGET_PATH="${0}_files/"',
68-
#' echo "Matched [standalone script] or [bazel test] context [${TARGET_PATH}]"',
69-
'',
7068
'elif [[ -e "${0}.runfiles/__main__/${PACKAGE_PATH}/${TARGET_NAME}/${NODE_EXE}" ]]; then',
71-
' TARGET_PATH="${0}.runfiles/__main__/${PACKAGE_PATH}/${TARGET_NAME}/"',
69+
' TARGET_PATH="${0}.runfiles/__main__/${PACKAGE_PATH}/${TARGET_NAME}/"',
7270
#' echo "Matched [bazel run] context [${TARGET_PATH}]"',
7371
'',
72+
'elif [[ -e "${0}.runfiles/${WORKSPACE_NAME}/${PACKAGE_PATH}/${TARGET_NAME}/${NODE_EXE}" ]]; then',
73+
' TARGET_PATH="${0}.runfiles/${WORKSPACE_NAME}/${PACKAGE_PATH}/${TARGET_NAME}/"',
74+
#' echo "Matched [bazel build] or [bazel run] context in a named workspace [${TARGET_PATH}]"',
75+
'',
76+
'elif [[ -e "${0}.runfiles/${PACKAGE_PATH}/${TARGET_NAME}/${NODE_EXE}" ]]; then',
77+
' TARGET_PATH="${0}.runfiles/${PACKAGE_PATH}/${TARGET_NAME}/"',
78+
#' echo "Matched [bazel build] context [${TARGET_PATH}]"',
79+
'',
7480
'elif [[ -e "${PACKAGE_PATH}/${TARGET_NAME}/${NODE_EXE}" ]]; then',
7581
' TARGET_PATH="${PACKAGE_PATH}/${TARGET_NAME}/"',
7682
#' echo "Matched [bazel test TESTRULE] context [${TARGET_PATH}]"',

node/internal/parse_yarn_lock.js

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,19 @@ function main() {
2626
//
2727
const entries = Object.keys(yarn.object).map(key => makeYarnEntry(key, yarn.object[key]));
2828

29-
// For all top-level folders in the node_modules directory that
30-
// contain a package.json file...
31-
const getModulesIn = p => fs.readdirSync(p)
32-
.filter(f =>
33-
fs.statSync(path.join(p, f)).isDirectory() &&
34-
fs.existsSync(path.join(p, f, 'package.json')) &&
35-
fs.statSync(path.join(p, f, 'package.json')).isFile());
36-
37-
// ... parse ithem
38-
const modules = getModulesIn('node_modules').map(dir => parseNodeModulePackageJson(dir));
39-
40-
// Iterate all the modules and merge the information from yarn into
41-
// the module
29+
// Scan the node_modules directory and find all top-level ('foo') or scoped (@bar/baz)
30+
// modules, i.e. folders which contain a package.json file...
31+
const getModulesIn = p => fs.readdirSync(p).filter(f => isPackage(p, undefined, f));
32+
const findScopes = p => fs.readdirSync(p).filter(f => f.startsWith("@") && fs.statSync(path.join(p, f)).isDirectory());
33+
const getModulesInScope = (p, s) => fs.readdirSync(path.join(p, s)).filter(f => isPackage(p, s, f));
34+
35+
// ...then parse the package.json files to collect the metadata
36+
let topLevelModuleDirs = getModulesIn('node_modules');
37+
let scopeModuleDirs = findScopes('node_modules').map(scope => getModulesInScope('node_modules', scope).map(m => scope+'/'+m)).reduce((a, b) => a.concat(b), []);
38+
let moduleDirs = topLevelModuleDirs.concat(scopeModuleDirs);
39+
const modules = moduleDirs.map(dir => parseNodeModulePackageJson(dir));
40+
41+
// Iterate all the modules and merge the information from yarn into the module
4242
modules.forEach(module => mergePackageJsonWithYarnEntry(entries, module));
4343

4444
// Didn't realize that the nodejs module ecosystem can contain
@@ -68,6 +68,12 @@ function main() {
6868
print("# EOF");
6969
}
7070

71+
function isPackage(p, s, f) {
72+
let dir = s ? path.join(p, s, f) : path.join(p, f);
73+
return fs.statSync(dir).isDirectory() &&
74+
fs.existsSync(path.join(dir, 'package.json')) &&
75+
fs.statSync(path.join(dir, 'package.json')).isFile()
76+
}
7177

7278
/**
7379
* Given a list of yarn entries and a target module, find an exact
@@ -202,9 +208,10 @@ function breakCircularDependencies(modules) {
202208
}
203209
});
204210

205-
// Each entry in the cluster must have no other outgoing
206-
// dependencies
207-
entry.deps = new Set();
211+
// Each entry in the cluster must have no connections to other
212+
// dependencies in the cluster, or on the cluster pseudo-dep
213+
pseudo.deps.forEach(circ => entry.deps.delete(circ));
214+
entry.deps.delete(pseudo);
208215
});
209216

210217
// Store this new pseudo-module in the modules list
@@ -278,17 +285,22 @@ function printNodeModule(module) {
278285
print(``);
279286
printJson(module);
280287
print(`node_module(`);
281-
print(` name = "${module.name}",`);
288+
print(` name = "${module.yarn ? module.yarn.label : module.name}",`);
282289

283290
// SCC pseudomodule wont have 'yarn' property
284291
if (module.yarn) {
285292
const url = module.yarn.url || module.url;
286293
const sha1 = module.yarn.sha1;
287294
const executables = module.executables;
288-
295+
296+
print(` module_name = "${module.name}",`);
289297
print(` version = "${module.version}",`);
290298
print(` package_json = "node_modules/${module.name}/package.json",`);
291-
print(` srcs = glob(["node_modules/${module.name}/**/*"], exclude = ["node_modules/${module.name}/package.json"]),`);
299+
// Exclude filenames with spaces: Bazel can't cope with them (we just have to hope they aren't needed later...)
300+
print(` srcs = glob(["node_modules/${module.name}/**/*"], exclude = [
301+
"node_modules/${module.name}/package.json",
302+
"**/* *",
303+
]),`);
292304
if (url) {
293305
print(` url = "${url}",`);
294306
}
@@ -303,12 +315,12 @@ function printNodeModule(module) {
303315
}
304316
print(` },`);
305317
}
306-
307318
}
319+
308320
if (deps && deps.size) {
309321
print(` deps = [`);
310322
deps.forEach(dep => {
311-
print(` ":${dep.name}",`);
323+
print(` ":${dep.yarn ? dep.yarn.label : dep.name}",`);
312324
});
313325
print(` ],`);
314326
}
@@ -327,7 +339,7 @@ function printNodeModuleAll(modules) {
327339
print(` name = "_all_",`);
328340
print(` deps = [`);
329341
modules.forEach(module => {
330-
print(` ":${module.name}",`);
342+
print(` ":${module.yarn ? module.yarn.label : module.name}",`);
331343
});
332344
print(` ],`);
333345
print(`)`);

tests/polymer-cli/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
sh_test(
4+
name = "polymer-cli_bin_test",
5+
size = "small",
6+
srcs = ["polymer-cli_bin_test.sh"],
7+
data = ["@yarn_modules//:polymer-cli_polymer_bin"],
8+
)
9+

tests/polymer-cli/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Polymer CLI Example
2+
3+
This folder demonstrates that the generated `BUILD` file for
4+
`@yarn_modules` contains a `node_binary` rule for the polymer-cli module
5+
executable. It tests that the target is callable and that the help
6+
message is output.
7+
8+
Polymer CLI is a fairly complex dependency that contains @-scoped dependencies
9+
and complex cyclic dependencies.
10+
11+
```sh
12+
# Should be able to run polymer-cli directly
13+
$ bazel build @yarn_modules//:polymer-cli_polymer_bin -- --help
14+
15+
# Should be able to invoke polymer-cli as standalone script
16+
$ ./bazel-bin/external/yarn_modules/polymer-cli_polymer_bin --help
17+
```

tests/polymer-cli/WORKSPACE

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
workspace(name = "org_pubref_rules_node_polymer_test")
2+
3+
local_repository(
4+
name = "org_pubref_rules_node",
5+
path = "../..",
6+
)
7+
8+
load("@org_pubref_rules_node//node:rules.bzl", "node_repositories", "yarn_modules")
9+
10+
node_repositories()
11+
12+
yarn_modules(
13+
name = "yarn_modules",
14+
deps = {
15+
"polymer-cli": "1.5.7",
16+
},
17+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set -e
2+
3+
if (./external/yarn_modules/polymer-cli_polymer_bin --help &) | grep -qF '/__\/ /__\/ \/__\ The multi-tool for Polymer projects'; then
4+
echo "PASS"
5+
else
6+
exit 1
7+
fi

0 commit comments

Comments
 (0)