Created
October 16, 2020 18:30
-
-
Save mattem/f6e85437b0dbcca661013a19247889a9 to your computer and use it in GitHub Desktop.
karma web tests with Chrome and ChromeHeadless with bazel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// THIS FILE IS AUTO GENERATED BY TEMPL_TARGET | |
// DO NOT EDIT | |
const path = require('path'); | |
const child_process = require('child_process'); | |
const runfiles = require(process.env.BAZEL_NODE_RUNFILES_HELPER); | |
/** | |
* Helper function to find a particular namedFile | |
* within the webTestMetadata webTestFiles | |
*/ | |
function findNamedFile(webTestMetadata, key) { | |
let result; | |
webTestMetadata.webTestFiles.forEach(entry => { | |
const webTestNamedFiles = entry.namedFiles; | |
if (webTestNamedFiles && webTestNamedFiles[key]) { | |
result = webTestNamedFiles[key]; | |
} | |
}); | |
return result; | |
} | |
/** | |
* Helper function to extract a browser archive | |
* and return the path to extract executable | |
*/ | |
function extractWebArchive(extractExe, archiveFile, executablePath) { | |
try { | |
// Paths are relative to the root runfiles folder | |
extractExe = extractExe ? path.join('..', extractExe) : extractExe; | |
archiveFile = path.join('..', archiveFile); | |
const extractedExecutablePath = path.join(process.cwd(), executablePath); | |
if (!extractExe) { | |
throw new Error('No EXTRACT_EXE found'); | |
} | |
child_process.execFileSync( | |
extractExe, [archiveFile, '.'], {stdio: [process.stdin, process.stdout, process.stderr]}); | |
return extractedExecutablePath; | |
} catch (e) { | |
console.error(`Failed to extract ${archiveFile}`); | |
throw e; | |
} | |
} | |
/** | |
* Check if Chrome sandboxing is supported on the current platform. | |
*/ | |
function supportChromeSandboxing() { | |
if (process.platform === 'darwin') { | |
// Chrome 73+ fails to initialize the sandbox on OSX when running under Bazel. | |
// ``` | |
// ERROR [launcher]: Cannot start ChromeHeadless | |
// ERROR:crash_report_database_mac.mm(96)] mkdir | |
// /private/var/tmp/_bazel_greg/62ef096b0da251c6d093468a1efbfbd3/execroot/angular/bazel-out/darwin-fastbuild/bin/external/io_bazel_rules_webtesting/third_party/chromium/chromium.out/chrome-mac/Chromium.app/Contents/Versions/73.0.3683.0/Chromium | |
// Framework.framework/Versions/A/new: Permission denied (13) ERROR:file_io.cc(89)] | |
// ReadExactly: expected 8, observed 0 ERROR:crash_report_database_mac.mm(96)] mkdir | |
// /private/var/tmp/_bazel_greg/62ef096b0da251c6d093468a1efbfbd3/execroot/angular/bazel-out/darwin-fastbuild/bin/external/io_bazel_rules_webtesting/third_party/chromium/chromium.out/chrome-mac/Chromium.app/Contents/Versions/73.0.3683.0/Chromium | |
// Framework.framework/Versions/A/new: Permission denied (13) Chromium Helper[94642] <Error>: | |
// SeatbeltExecServer: Failed to initialize sandbox: -1 Operation not permitted Failed to | |
// initialize sandbox. [0213/201206.137114:FATAL:content_main_delegate.cc(54)] Check failed: | |
// false. 0 Chromium Framework 0x000000010c078bc9 ChromeMain + 43788137 1 | |
// Chromium Framework 0x000000010bfc0f43 ChromeMain + 43035363 | |
// ... | |
// ``` | |
return false; | |
} | |
if (process.platform === 'linux') { | |
// Chrome on Linux uses sandboxing, which needs user namespaces to be enabled. | |
// This is not available on all kernels and it might be turned off even if it is available. | |
// Notable examples where user namespaces are not available include: | |
// - In Debian it is compiled-in but disabled by default. | |
// - The Docker daemon for Windows or OSX does not support user namespaces. | |
// We can detect if user namespaces are supported via | |
// /proc/sys/kernel/unprivileged_userns_clone. For more information see: | |
// https://github.com/Googlechrome/puppeteer/issues/290 | |
// https://superuser.com/questions/1094597/enable-user-namespaces-in-debian-kernel#1122977 | |
// https://github.com/karma-runner/karma-chrome-launcher/issues/158 | |
// https://github.com/angular/angular/pull/24906 | |
try { | |
const res = child_process.execSync('cat /proc/sys/kernel/unprivileged_userns_clone') | |
.toString() | |
.trim(); | |
return res === '1'; | |
} catch (error) { | |
} | |
return false; | |
} | |
return true; | |
} | |
function setChromeBrowserForEnv(conf) { | |
// when using bazel run, BUILD_WORKSPACE_DIRECTORY is set to the the absolute path of the bazel workspace | |
// however we also want to run in headless when running remotely and the user is SSH'd to the remote machine | |
// when using bazel test, this env var is not set | |
// this allows us to use chrome for debugging with the run verb | |
const browser = process.env.BUILD_WORKSPACE_DIRECTORY && !process.env.SSH_CONNECTION ? 'Chrome' : 'ChromeHeadless'; | |
const webTestMetadata = require(runfiles.resolve(process.env['WEB_TEST_METADATA'])); | |
if (webTestMetadata.environment === 'local') { | |
// When a local chrome (or firefox) browser is chosen such as | |
// "@io_bazel_rules_webtesting//browsers:chromium-local" or | |
// "@io_bazel_rules_webtesting//browsers:firefox-local" | |
// then the 'environment' will equal 'local' and | |
// 'webTestFiles' will contain the path to the binary to use | |
const extractExe = findNamedFile(webTestMetadata, 'EXTRACT_EXE'); | |
webTestMetadata.webTestFiles.forEach(webTestFiles => { | |
const webTestNamedFiles = webTestFiles.namedFiles; | |
const archiveFile = webTestFiles.archiveFile; | |
if (webTestNamedFiles.CHROMIUM) { | |
// When karma is configured to use Chrome it will look for a CHROME_BIN | |
// environment variable. | |
if (archiveFile) { | |
process.env.CHROME_BIN = extractWebArchive(extractExe, archiveFile, webTestNamedFiles.CHROMIUM); | |
} else { | |
try { | |
process.env.CHROME_BIN = runfiles.resolve(webTestNamedFiles.CHROMIUM); | |
} catch { | |
// Fail as this file is expected to be in runfiles | |
throw new Error(`Failed to resolve rules_webtesting Chromium binary '${webTestNamedFiles.CHROMIUM}' in runfiles`); | |
} | |
} | |
} | |
}); | |
} | |
if (!supportChromeSandboxing()) { | |
const launcher = 'CustomChromeWithoutSandbox'; | |
conf.customLaunchers = {[launcher]: {base: browser, flags: ['--no-sandbox']}}; | |
conf.browsers.push(launcher); | |
} else { | |
conf.browsers.push(browser); | |
} | |
} | |
function setIsSingleRun(conf) { | |
// for bazel run, we don't want a single run, and instead we want to keep the browser open for debugging | |
// for test, run the test and close right away | |
conf.singleRun = !process.env.BUILD_WORKSPACE_DIRECTORY; | |
} | |
function setIsAutoWatch(conf) { | |
conf.autoWatch = process.env.IBAZEL_NOTIFY_CHANGES === 'y'; | |
} | |
module.exports = function(config) { | |
const conf = { | |
// base path that will be used to resolve all patterns (eg. files, exclude) | |
basePath: '.', | |
// frameworks to use | |
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter | |
frameworks: ['jasmine'], | |
// list of files / patterns to load in the browser | |
files: [ | |
{ pattern: runfiles.resolve('npm/node_modules/zone.js/dist/zone-testing-bundle.js') }, | |
{ pattern: runfiles.resolve('npm/node_modules/reflect-metadata/Reflect.js') }, | |
{ pattern: runfiles.resolve(path.posix.join(process.env.BAZEL_WORKSPACE, 'TEMPL_TEST_BUNDLE')), type: 'module' }, | |
{ pattern: runfiles.resolve(path.posix.join(process.env.BAZEL_WORKSPACE, 'TEMPL_MAP_TEST_BUNDLE')), included: false, watched: false } | |
], | |
// preprocess matching files before serving them to the browser | |
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor | |
preprocessors: { | |
'**/*.js': ['sourcemap'] | |
}, | |
// test results reporter to use | |
// possible values: 'dots', 'progress' | |
// available reporters: https://npmjs.org/browse/keyword/karma-reporter | |
reporters: ['progress'], | |
// web server port | |
port: 9876, | |
// enable / disable colors in the output (reporters and logs) | |
colors: true, | |
// level of logging | |
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG | |
logLevel: config.LOG_INFO, | |
// start these browsers | |
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher | |
// this is set later by setChromeBrowserForEnv | |
browsers: [], | |
// Concurrency level | |
// how many browser should be started simultaneous | |
concurrency: Infinity | |
}; | |
setChromeBrowserForEnv(conf); | |
setIsSingleRun(conf); | |
setIsAutoWatch(conf); | |
config.set(conf); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
load("//ev/tooling/bazel/rules_typescript:ts_library.bzl", _ts_library = "ts_library") | |
load("//ev/tooling/bazel/rules_ev_web:esbuild.bzl", _ev_es_build = "ev_es_build") | |
load("//ev/tooling/bazel/ev_lib:expand_template.bzl", _ev_expand_template = "ev_expand_template") | |
load("@io_bazel_rules_webtesting//web:web.bzl", _web_test_suite = "web_test_suite") | |
load("@bazel_skylib//rules:write_file.bzl", _write_file = "write_file") | |
load("@npm//karma:index.bzl", _karma = "karma") | |
def generate_test_entry_point(name, specs, test_library): | |
entry_point = "%s.bootstrap.ts" % name | |
_write_file( | |
name = "%s_bootstrap_ts_file" % name, | |
content = [ | |
"// THIS FILE IS GENERATED BY %s DO NOT EDIT" % name, | |
"declare const __karma__: any;", | |
"__karma__.loaded = () => {};", | |
] + [ | |
"import './%s';" % spec[:-3] | |
for spec in specs if spec.endswith('spec.ts') | |
] + [ | |
"__karma__.start();", | |
], | |
out = entry_point, | |
) | |
entry_point_library = "%s_bootstrap" % name | |
_ts_library( | |
name = entry_point_library, | |
testonly = 1, | |
srcs = [ | |
":%s_bootstrap_ts_file" % name, | |
], | |
linting = False, | |
deps = [ | |
":%s" % test_library, | |
], | |
) | |
return entry_point, entry_point_library | |
def generate_testing_bundle(name, entry_point, deps): | |
_ev_es_build( | |
name = "%s_bundle" % name, | |
testonly = 1, | |
entry_point = entry_point, | |
deps = deps, | |
format = "esm", | |
) | |
def generate_karma_runner(name): | |
_ev_expand_template( | |
name = "%s_karma.conf" % name, | |
testonly = 1, | |
template = "//ev/tooling/bazel/rules_ev_web:_karma.conf.js", | |
substitutions = { | |
"TEMPL_TARGET": name, | |
"TEMPL_TEST_BUNDLE": "%s/%s_bundle.js" % (native.package_name(), name), | |
"TEMPL_MAP_TEST_BUNDLE": "%s/%s_bundle.js.map" % (native.package_name(), name), | |
}, | |
data = [ | |
":%s_bundle.js" % name, | |
":%s_bundle.js.map" % name, | |
], | |
out = "%s.karma.conf.js" % name, | |
) | |
wrapped_test_name = "%s_karma" % name | |
_karma( | |
name = wrapped_test_name, | |
testonly = 1, | |
data = [ | |
"%s_karma.conf" % name, | |
":%s_bundle.js" % name, | |
":%s_bundle.js.map" % name, | |
"@npm//karma-jasmine", | |
"@npm//karma-chrome-launcher", | |
"@npm//karma-sourcemap-loader", | |
"@npm//:node_modules/zone.js/dist/zone-testing-bundle.js", | |
"@npm//:node_modules/reflect-metadata/Reflect.js", | |
], | |
templated_args = [ | |
"start", | |
"$(rootpath %s_karma.conf)" % name, | |
], | |
tags = ["native"], | |
) | |
return wrapped_test_name | |
def ev_ts_web_test(name, srcs = [], deps = [], size = "large", test_suite_tags = [], linting = True, **kwargs): | |
test_library = "%s_lib" % name | |
_ts_library( | |
name = test_library, | |
testonly = 1, | |
srcs = srcs, | |
deps = [ | |
"@npm//@types/jasmine", | |
"@npm//rxjs", | |
] + deps, | |
linting = linting, | |
) | |
entry_point, entry_point_library = generate_test_entry_point(name, srcs, test_library) | |
generate_testing_bundle(name, entry_point, [entry_point_library]) | |
wrapped_test_name = generate_karma_runner(name) | |
tags = kwargs.pop("tags", []) + [ | |
# Users don't need to know that this tag is required to run under ibazel | |
"ibazel_notify_changes", | |
] | |
# rules_webesting requires the "native" tag for browsers | |
if not "native" in tags: | |
tags = tags + ["native"] | |
_web_test_suite( | |
name = name, | |
args = kwargs.pop("args", None), | |
flaky = kwargs.pop("flaky", None), | |
local = kwargs.pop("local", None), | |
shard_count = kwargs.pop("shard_count", None), | |
size = kwargs.pop("size", "large"), | |
timeout = kwargs.pop("timeout", None), | |
launcher = ":" + wrapped_test_name, | |
browsers = ["@io_bazel_rules_webtesting//browsers:chromium-local"], | |
browser_overrides = kwargs.pop("browser_overrides", None), | |
config = kwargs.pop("config", None), | |
data = kwargs.pop("web_test_data", []), | |
tags = tags, | |
test = ":" + wrapped_test_name, | |
test_suite_tags = kwargs.pop("test_suite_tags", None), | |
visibility = kwargs.pop("visibility", None), | |
) |
Author
mattem
commented
Oct 16, 2020
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment