mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
414 lines
15 KiB
JavaScript
414 lines
15 KiB
JavaScript
// Karma configuration GENERATED BY Bazel
|
|
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const child_process = require('child_process');
|
|
const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']);
|
|
|
|
// Static files are added to the files array but configured to not be included,
|
|
// so karma does not included them. Note that we need.
|
|
const STATIC_FILES = [
|
|
// BEGIN STATIC FILES
|
|
TMPL_static_files
|
|
// END STATIC FILES
|
|
];
|
|
|
|
const BOOTSTRAP_FILES = [
|
|
// BEGIN BOOTSTRAP FILES
|
|
TMPL_bootstrap_files
|
|
// END BOOTSTRAP FILES
|
|
];
|
|
|
|
// Test + runtime entry point files
|
|
const BUNDLE_FILES = findAllFiles('TMPL_test_bundle_dir');
|
|
|
|
// process.env.BUILD_WORKSPACE_DIRECTORY is set to the the absolute path of
|
|
// the bazel workspace when doing `bazel run ...`
|
|
// See https://docs.bazel.build/versions/master/user-manual.html#run
|
|
const isBazelRun = !!process.env.BUILD_WORKSPACE_DIRECTORY;
|
|
|
|
// Setup ibazel livereload when run with ibazel
|
|
const ibazelNotifyChanges = isBazelRun && !!process.env.IBAZEL_NOTIFY_CHANGES;
|
|
|
|
const VERBOSE_LOGS = !!process.env['VERBOSE_LOGS'];
|
|
|
|
function log_verbose(...m) {
|
|
// This is a template file so we use __filename to output the actual filename
|
|
if (VERBOSE_LOGS) console.error(`[${path.basename(__filename)}]`, ...m);
|
|
}
|
|
|
|
function findAllFiles(dir, subdir, found) {
|
|
subdir = subdir || '.';
|
|
found = found || [];
|
|
|
|
const fullPath = runfiles.resolve(dir);
|
|
|
|
for (const file of fs.readdirSync(fullPath)) {
|
|
const filePath = dir + '/' + file;
|
|
|
|
if (fs.statSync(runfiles.resolve(filePath)).isDirectory()) {
|
|
findAllFiles(filePath, subdir, found);
|
|
} else {
|
|
found.push(filePath);
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
/**
|
|
* Helper function to merge base karma config values that are arrays.
|
|
*/
|
|
function mergeConfigArray(conf, name, values) {
|
|
if (!conf[name]) {
|
|
conf[name] = [];
|
|
}
|
|
values.forEach(v => {
|
|
if (!conf[name].includes(v)) {
|
|
conf[name].push(v);
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 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]});
|
|
log_verbose(
|
|
`Extracting web archive ${archiveFile} with ${extractExe} to ${extractedExecutablePath}`);
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Helper function to override nested karma config values.
|
|
*/
|
|
function overrideNestedConfigValue(conf, name, value) {
|
|
const nameParts = name.split('.');
|
|
const finalName = nameParts.pop();
|
|
for (const property of nameParts) {
|
|
if (!(property in conf)) {
|
|
conf[property] = {};
|
|
}
|
|
conf = conf[property];
|
|
}
|
|
if (conf.hasOwnProperty(name)) {
|
|
console.warn(`Your karma configuration specifies '${name}' which will be overwritten by Bazel`);
|
|
}
|
|
conf[finalName] = value;
|
|
}
|
|
|
|
/**
|
|
* Configuration settings for karma under Bazel common to karma_web_test
|
|
* and karma_web_test_suite.
|
|
*/
|
|
function configureTestReporters(conf) {
|
|
// Configure the junit reporter if the XML_OUTPUT_FILE environment is defined.
|
|
const testOutputFile = process.env.XML_OUTPUT_FILE;
|
|
if (testOutputFile) {
|
|
mergeConfigArray(conf, 'plugins', []);
|
|
|
|
mergeConfigArray(conf, 'reporters', ['junit']);
|
|
|
|
|
|
overrideNestedConfigValue(conf, 'junitReporter.outputDir', path.dirname(testOutputFile));
|
|
overrideNestedConfigValue(conf, 'junitReporter.outputFile', path.basename(testOutputFile));
|
|
overrideNestedConfigValue(conf, 'junitReporter.useBrowserName', false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configure the 'files' and 'proxies' configuration attributes based on the
|
|
* injected STATIC_FILES, BOOTSTRAP_FILES and BUNDLE_FILES.
|
|
*/
|
|
function configureFiles(conf) {
|
|
// Static files available but not included
|
|
STATIC_FILES.forEach(f => conf.files.push({pattern: f, included: false}));
|
|
|
|
// Bootstrap files included before spec files
|
|
BOOTSTRAP_FILES.forEach(f => conf.files.push(f));
|
|
|
|
// Bundle files available to downloaded, included if non-chunk js files
|
|
BUNDLE_FILES.forEach(f => {
|
|
const isJs = f.endsWith('.js') || f.endsWith('.mjs');
|
|
const isChunk = isJs && f.includes('chunk-');
|
|
|
|
conf.files.push({
|
|
pattern: f,
|
|
type: isJs ? 'module' : undefined,
|
|
included: isJs && !isChunk,
|
|
});
|
|
});
|
|
|
|
// Proxy simple URLs to the bazel resolved files
|
|
[...STATIC_FILES, ...BOOTSTRAP_FILES, ...BUNDLE_FILES].forEach(f => {
|
|
// In Windows, the runfile will probably not be symlinked. Se we need to
|
|
// serve the real file through karma, and proxy calls to the expected file
|
|
// location in the runfiles to the real file.
|
|
const resolvedFile = runfiles.resolve(f);
|
|
|
|
// Prefixing the proxy path with '/absolute' allows karma to load local
|
|
// files. This doesn't see to be an official API.
|
|
// https://github.com/karma-runner/karma/issues/2703
|
|
conf.proxies['/base/' + f] = '/absolute' + resolvedFile;
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Configure karma when run under web_test_suite to use the test suite provided
|
|
* browsers.
|
|
*/
|
|
function configureWebTestSuiteBrowsers(conf) {
|
|
// If this is NOT invoked via a web_test_suite rule exist and assume configured elsewhere
|
|
if (!process.env['WEB_TEST_METADATA']) {
|
|
return;
|
|
}
|
|
|
|
const webTestMetadata = require(runfiles.resolve(process.env['WEB_TEST_METADATA']));
|
|
|
|
log_verbose(`WEB_TEST_METADATA: ${JSON.stringify(webTestMetadata, null, 2)}`);
|
|
|
|
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`);
|
|
}
|
|
}
|
|
// Read any additional chrome options (as specified by the
|
|
// rules_webtesting manifest).
|
|
const chromeOptions = (webTestMetadata['capabilities'] || {})['goog:chromeOptions'];
|
|
const additionalArgs = (chromeOptions ? chromeOptions['args'] : []).filter(arg => {
|
|
// We never want to 'run' Chrome in headless mode.
|
|
return arg != '--headless';
|
|
});
|
|
const browser = process.env['DISPLAY'] ? 'Chrome' : 'ChromeHeadless';
|
|
if (!supportChromeSandboxing()) {
|
|
const launcher = 'CustomChromeWithoutSandbox';
|
|
conf.customLaunchers =
|
|
{[launcher]: {base: browser, flags: ['--no-sandbox', ...additionalArgs]}};
|
|
conf.browsers.push(launcher);
|
|
} else {
|
|
const launcher = 'CustomChrome';
|
|
conf.customLaunchers = {[launcher]: {base: browser, flags: additionalArgs}};
|
|
conf.browsers.push(launcher);
|
|
}
|
|
}
|
|
if (webTestNamedFiles['FIREFOX']) {
|
|
// When karma is configured to use Firefox it will look for a
|
|
// FIREFOX_BIN environment variable.
|
|
if (archiveFile) {
|
|
process.env.FIREFOX_BIN =
|
|
extractWebArchive(extractExe, archiveFile, webTestNamedFiles['FIREFOX']);
|
|
} else {
|
|
try {
|
|
process.env.FIREFOX_BIN = runfiles.resolve(webTestNamedFiles['FIREFOX']);
|
|
} catch {
|
|
// Fail as this file is expected to be in runfiles
|
|
throw new Error(`Failed to resolve rules_webtesting Firefox binary '${
|
|
webTestNamedFiles['FIREFOX']}' in runfiles`);
|
|
}
|
|
}
|
|
conf.browsers.push(process.env['DISPLAY'] ? 'Firefox' : 'FirefoxHeadless');
|
|
}
|
|
});
|
|
} else {
|
|
throw new Error(`Unknown WEB_TEST_METADATA environment '${webTestMetadata['environment']}'`);
|
|
}
|
|
|
|
if (!conf.browsers.length) {
|
|
throw new Error('No browsers configured in web test suite');
|
|
}
|
|
}
|
|
|
|
function configureBazelTestBrowsers(conf) {
|
|
// If this is invoked via a web_test_suite rule exist and assume configured elsewhere
|
|
if (process.env['WEB_TEST_METADATA']) {
|
|
return;
|
|
}
|
|
|
|
// Fallback to using the system local chrome if no valid browsers have been
|
|
// configured above
|
|
if (!conf.browsers || !conf.browsers.length) {
|
|
console.warn('No browsers configured. Configuring Karma to use system Chrome.');
|
|
conf.browsers = [process.env['DISPLAY'] ? 'Chrome' : 'ChromeHeadless'];
|
|
}
|
|
}
|
|
|
|
function configureFormatError(conf) {
|
|
conf.formatError = (msg) => {
|
|
// This is a bazel specific formatError that removes the workspace
|
|
// name from stack traces.
|
|
// Look for filenames of the format "(<filename>:<row>:<column>"
|
|
const FILENAME_REGEX = /\(([^:\n\r]+)(:\d+:\d+)/gm;
|
|
msg = msg.replace(FILENAME_REGEX, (_, p1, p2) => {
|
|
if (p1.startsWith('../')) {
|
|
// Remove all leading "../"
|
|
while (p1.startsWith('../')) {
|
|
p1 = p1.substr(3);
|
|
}
|
|
} else {
|
|
// Remove workspace name(angular, ngdeps etc.) from the beginning.
|
|
const index = p1.indexOf('/');
|
|
if (index >= 0) {
|
|
p1 = p1.substr(index + 1);
|
|
}
|
|
}
|
|
return '(' + p1 + p2;
|
|
});
|
|
return msg + '\n\n';
|
|
};
|
|
}
|
|
|
|
module.exports = function(config) {
|
|
const conf = {
|
|
basePath: 'TMPL_runfiles_path',
|
|
|
|
colors: true,
|
|
logLevel: VERBOSE_LOGS ? config.LOG_DEBUG : config.LOG_INFO,
|
|
|
|
frameworks: ['jasmine'],
|
|
reporters: isBazelRun ? [] : ['progress'],
|
|
plugins: [
|
|
require('karma-chrome-launcher'), require('karma-firefox-launcher'), require('karma-jasmine'),
|
|
require('karma-sourcemap-loader'), require('karma-junit-reporter')
|
|
],
|
|
|
|
preprocessors: {
|
|
'**/*.js': ['sourcemap'],
|
|
},
|
|
|
|
concurrency: Infinity,
|
|
|
|
// When doing `bazel run ...` we want karma to continue running, where when
|
|
// doing `bazel test ...` it can run once and close right away.
|
|
singleRun: !isBazelRun,
|
|
|
|
// watch the files when using ibazel
|
|
autoWatch: ibazelNotifyChanges,
|
|
|
|
// Disable 'strict-origin-when-cross-origin' to allow external scripts for livereload
|
|
crossOriginAttribute: !ibazelNotifyChanges,
|
|
|
|
// Configured based on bazel web/testing setup
|
|
browsers: [],
|
|
|
|
// Configured based on bazel file inputs
|
|
// Include the ibazel livereload script at the start when available
|
|
files: process.env.IBAZEL_LIVERELOAD_URL ? [process.env.IBAZEL_LIVERELOAD_URL] : [],
|
|
|
|
// configured based on bazel file inputs
|
|
proxies: [],
|
|
};
|
|
|
|
configureTestReporters(conf);
|
|
configureFiles(conf);
|
|
configureFormatError(conf);
|
|
|
|
// Do not configure browsers for a `bazel run` and allow manually opening the browser
|
|
// for debugging.
|
|
if (!isBazelRun) {
|
|
configureWebTestSuiteBrowsers(conf);
|
|
configureBazelTestBrowsers(conf);
|
|
}
|
|
|
|
log_verbose(`karma configuration: ${JSON.stringify(conf, null, 2)}`);
|
|
|
|
config.set(conf);
|
|
}
|