diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index 04af29495..d23e493f2 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -11,6 +11,7 @@ filegroup( "//bazel/esbuild:files", "//bazel/http-server:files", "//bazel/integration:files", + "//bazel/karma:files", "//bazel/remote-execution:files", "//bazel/spec-bundling:files", ], diff --git a/bazel/karma/BUILD.bazel b/bazel/karma/BUILD.bazel new file mode 100644 index 000000000..d85c98bd8 --- /dev/null +++ b/bazel/karma/BUILD.bazel @@ -0,0 +1,9 @@ +package(default_visibility = ["//visibility:public"]) + +exports_files(["karma-debug-config.js"]) + +# Make source files available for distribution via pkg_npm +filegroup( + name = "files", + srcs = glob(["*"]), +) diff --git a/bazel/karma/index.bzl b/bazel/karma/index.bzl new file mode 100644 index 000000000..1ecb2598f --- /dev/null +++ b/bazel/karma/index.bzl @@ -0,0 +1,52 @@ +load( + "@npm//@bazel/concatjs:index.bzl", + _karma_web_test = "karma_web_test", + _karma_web_test_suite = "karma_web_test_suite", +) + +def _karma_debug_browsers_target(name, **web_test_args): + """Macro for defining a standalone karma web test target that starts Karma + without a browser, allowing for manual debugging.""" + + # Custom standalone web test that can be run to test against any browser + # that is manually connected to. + _karma_web_test( + name = "%s_bin" % name, + config_file = "//bazel/karma:karma-debug-config.js", + tags = ["manual"], + **web_test_args + ) + + # Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1429 + native.sh_test( + name = name, + srcs = ["%s_bin" % name], + tags = ["manual", "local", "ibazel_notify_changes"], + testonly = True, + ) + +def karma_web_test_suite(name, **kwargs): + """Wrapper for the default `karma_web_test_suite` with additional default browsers, + and a local target to ease debugging.""" + + # Set up default browsers if no explicit `browsers` have been specified. + if not hasattr(kwargs, "browsers"): + kwargs["tags"] = ["native"] + kwargs.get("tags", []) + kwargs["browsers"] = [ + "//bazel/browsers/chromium:chromium", + "//bazel/browsers/firefox:firefox", + ] + + # Filter out options which are specific to "karma_web_test" targets. We cannot + # pass options like "browsers" to the debug web test target. + web_test_args = {} + for opt_name in kwargs.keys(): + if not opt_name in ["wrapped_test_tags", "browsers", "tags"]: + web_test_args[opt_name] = kwargs[opt_name] + + # Custom standalone web test that can be run to test against any + # browser that is manually connected to. + _karma_debug_browsers_target(name = "%s_debug" % name) + + # Default test suite with all configured browsers. + _karma_web_test_suite(name = name, **kwargs) diff --git a/bazel/karma/karma-debug-config.js b/bazel/karma/karma-debug-config.js new file mode 100644 index 000000000..fd3099833 --- /dev/null +++ b/bazel/karma/karma-debug-config.js @@ -0,0 +1,46 @@ +/** + * Karma configuration that is used by Bazel `karma_web_test` local target which intends + * to not launch any browser, enabling manual browser debugging. + */ + +module.exports = (config) => { + const overwrites = {}; + + // By default "@bazel/concatjs" configures Chrome as browser. Since we don't want + // to launch any browser at all, we overwrite the "browsers" option. Since the + // default config tries to extend the browsers array with "Chrome", we need to + // always return a new empty array. + Object.defineProperty(overwrites, 'browsers', { + get: () => [], + set: () => {}, + enumerable: true, + }); + + // Ensures that tests start executing once browsers have been manually connected. We need + // to use "defineProperty" because the default "@bazel/concatjs" config overwrites the option. + Object.defineProperty(overwrites, 'autoWatch', { + get: () => true, + set: () => {}, + enumerable: true, + }); + + // When not running with ibazel, do not set up the `@bazel/concatjs` watcher. This one + // relies on ibazel to write to the `stdin` interface. When running without ibazel, the + // watcher will kill concatjs since there is no data written to the `stdin` interface. + if (process.env['IBAZEL_NOTIFY_CHANGES'] !== 'y') { + // We pre-define a plugins array that captures registration of Karma plugins + // and unsets the watcher definitions so that no watcher can be configured. + overwrites.plugins = new KarmaPluginArrayWithoutWatchers(); + } + + config.set(overwrites); +}; + +class KarmaPluginArrayWithoutWatchers extends Array { + // The Bazel Karma rules only register new plugins using `.push`. + push(...plugins) { + plugins.filter((p) => typeof p === 'object').forEach((p) => delete p.watcher); + + super.push(...plugins); + } +}