lix-website/themes/lix/assets/bootstrap/node_modules/karma-browserstack-launcher/index.js
2024-04-26 22:49:34 -06:00

331 lines
8.9 KiB
JavaScript

var q = require('q')
var api = require('browserstack')
var BrowserStackTunnel = require('browserstacktunnel-wrapper')
var os = require('os')
var workerManager = require('./worker-manager')
var BrowserStackReporter = require('./browserstack-reporter')
var createBrowserStackTunnel = function (logger, config, emitter) {
var log = logger.create('launcher.browserstack')
var bsConfig = config.browserStack || {}
var bsBinaryBasePath = process.env.BROWSER_STACK_BINARY_BASE_PATH || bsConfig.binaryBasePath || null
var bsRunConfig = {
key: process.env.BROWSER_STACK_ACCESS_KEY || bsConfig.accessKey,
localIdentifier: bsConfig.localIdentifier || bsConfig.tunnelIdentifier,
jarFile: process.env.BROWSER_STACK_TUNNEL_JAR || bsConfig.jarFile,
hosts: [{
name: config.hostname,
port: config.port,
sslFlag: 0
}],
proxyHost: bsConfig.proxyHost || null,
proxyPort: bsConfig.proxyPort || null,
proxyUser: bsConfig.proxyUser || null,
proxyPass: bsConfig.proxyPass || null,
forcelocal: bsConfig.forcelocal || null,
enableLoggingForApi: bsConfig.enableLoggingForApi || null
}
if (bsConfig.startTunnel === false) {
bsConfig.tunnelIdentifier = bsRunConfig.localIdentifier
return q()
}
bsRunConfig.localIdentifier = bsRunConfig.localIdentifier || 'karma' + Math.random()
bsConfig.tunnelIdentifier = bsRunConfig.localIdentifier
if (bsBinaryBasePath) {
switch (os.platform()) {
case 'linux':
switch (os.arch()) {
case 'x64':
bsRunConfig.linux64Bin = bsBinaryBasePath
break
case 'ia32':
bsRunConfig.linux32Bin = bsBinaryBasePath
break
}
break
case 'darwin':
bsRunConfig.osxBin = bsBinaryBasePath
break
default:
bsRunConfig.win32Bin = bsBinaryBasePath
break
}
}
log.debug('Establishing the tunnel on %s:%s', config.hostname, config.port)
var deferred = q.defer()
var tunnel = new BrowserStackTunnel(bsRunConfig)
tunnel.start(function (error) {
if (error) {
log.error('Can not establish the tunnel.\n%s', error.toString())
deferred.reject(error)
} else {
log.debug('Tunnel established.')
deferred.resolve()
}
})
emitter.on('exit', function (done) {
log.debug('Shutting down the tunnel.')
tunnel.stop(function (error) {
if (error) {
log.error(error)
}
if (workerManager.isPolling) {
workerManager.stopPolling()
}
done()
})
})
return deferred.promise
}
var createBrowserStackClient = function (/* config.browserStack */config, /* BrowserStack:sessionMapping */ sessionMapping) {
var env = process.env
config = config || {}
var options = {
username: env.BROWSER_STACK_USERNAME || config.username,
password: env.BROWSER_STACK_ACCESS_KEY || config.accessKey
}
if (config.proxyHost && config.proxyPort) {
config.proxyProtocol = config.proxyProtocol || 'http'
var proxyAuth = (config.proxyUser && config.proxyPass)
? (encodeURIComponent(config.proxyUser) + ':' + encodeURIComponent(config.proxyPass) + '@') : ''
options.proxy = config.proxyProtocol + '://' + proxyAuth + config.proxyHost + ':' + config.proxyPort
}
sessionMapping.credentials = {
username: options.username,
password: options.password,
proxy: options.proxy
}
// TODO(vojta): handle no username/pwd
var client = api.createClient(options)
var pollingTimeout = config.pollingTimeout || 1000
if (!workerManager.isPolling) {
workerManager.startPolling(client, pollingTimeout, function (err) {
if (err) {
console.error(err)
}
})
}
return client
}
var formatError = function (error) {
if (error.message === 'Validation Failed') {
return ' Validation Failed: you probably misconfigured the browser ' +
'or given browser is not available.'
}
return error.toString()
}
var BrowserStackBrowser = function (
id, emitter, args, logger,
/* config */ config,
/* browserStackTunnel */ tunnel,
/* browserStackClient */ client,
baseLauncherDecorator,
captureTimeoutLauncherDecorator,
retryLauncherDecorator,
/* BrowserStack:sessionMapping */ sessionMapping
) {
var self = this
baseLauncherDecorator(self)
captureTimeoutLauncherDecorator(self)
retryLauncherDecorator(self)
var workerId = null
var captured = false
var alreadyKilling = null
var log = logger.create('launcher.browserstack')
var browserName = (args.browser || args.device) + (args.browser_version ? ' ' + args.browser_version : '') +
' (' + args.os + ' ' + args.os_version + ')'
this.id = id
this.name = browserName + ' on BrowserStack'
var bsConfig = config.browserStack || {}
var captureTimeout = config.captureTimeout || 0
var captureTimeoutId
var retryLimit = bsConfig.retryLimit || 3
var previousUrl = null
this.start = function (url) {
url = url || previousUrl
previousUrl = url
var globalSettings = Object.assign(
{
timeout: 300,
name: 'Karma test',
build: process.env.BUILD_NUMBER ||
process.env.BUILD_TAG ||
process.env.CI_BUILD_NUMBER ||
process.env.CI_BUILD_TAG ||
process.env.TRAVIS_BUILD_NUMBER ||
process.env.CIRCLE_BUILD_NUM ||
process.env.DRONE_BUILD_NUMBER || null,
// TODO(vojta): remove "version" (only for B-C)
browser_version: args.version || 'latest',
video: true
},
bsConfig
)
// TODO(vojta): handle non os/browser/version
var settings = Object.assign(
{
url: url + '?id=' + id,
'browserstack.tunnel': true
},
globalSettings,
args
)
tunnel.then(function () {
client.createWorker(settings, function (error, worker) {
var sessionUrlShowed = false
if (error) {
log.error('Can not start %s\n %s', browserName, formatError(error))
return emitter.emit('browser_process_failure', self)
}
workerId = worker.id
alreadyKilling = null
worker = workerManager.registerWorker(worker)
worker.on('status', function (status) {
// TODO(vojta): show immediately in createClient callback once this gets fixed:
// https://github.com/browserstack/api/issues/10
if (!sessionUrlShowed) {
log.info('%s session at %s', browserName, worker.browser_url)
sessionMapping[self.id] = worker.browser_url.split('/').slice(-1)[0]
sessionUrlShowed = true
}
switch (status) {
case 'running':
log.debug('%s job started with id %s', browserName, workerId)
if (captureTimeout && !captured) {
captureTimeoutId = setTimeout(self._onTimeout, captureTimeout)
}
break
case 'queue':
log.debug('%s job with id %s in queue.', browserName, workerId)
break
case 'delete':
log.debug('%s job with id %s has been deleted.', browserName, workerId)
break
}
})
})
}).catch(function () {
emitter.emit('browser_process_failure', self)
})
}
this.kill = function (done) {
var allDone = function () {
self._done()
if (done) {
done()
}
}
if (!alreadyKilling) {
alreadyKilling = q.defer()
if (workerId) {
log.debug('Killing %s (worker %s).', browserName, workerId)
client.terminateWorker(workerId, function () {
log.debug('%s (worker %s) successfully killed.', browserName, workerId)
if (captureTimeoutId) {
clearTimeout(captureTimeoutId)
captureTimeoutId = null
}
workerId = null
captured = false
alreadyKilling.resolve()
})
} else {
alreadyKilling.resolve()
}
}
return alreadyKilling.promise.then(allDone)
}
this.forceKill = function () {
var self = this
return q.promise(function (resolve) {
self.kill(resolve)
})
}
this.markCaptured = function () {
captured = true
if (captureTimeoutId) {
clearTimeout(captureTimeoutId)
captureTimeoutId = null
}
}
this.isCaptured = function () {
return captured
}
this.toString = function () {
return this.name
}
this._onTimeout = function () {
if (captured) {
return
}
log.warn('%s has not captured in %d ms, killing.', browserName, captureTimeout)
self.kill(function () {
if (retryLimit--) {
self.start(previousUrl)
} else {
emitter.emit('browser_process_failure', self)
}
})
}
}
// PUBLISH DI MODULE
module.exports = {
'browserStackTunnel': ['factory', createBrowserStackTunnel],
'browserStackClient': ['factory', createBrowserStackClient],
'launcher:BrowserStack': ['type', BrowserStackBrowser],
'reporter:BrowserStack': ['type', BrowserStackReporter],
'BrowserStack:sessionMapping': ['value', {}]
}