Updated the files.
This commit is contained in:
parent
1553e6b971
commit
753967d4f5
23418 changed files with 3784666 additions and 0 deletions
271
my-app/node_modules/karma/lib/browser.js
generated
vendored
Executable file
271
my-app/node_modules/karma/lib/browser.js
generated
vendored
Executable file
|
@ -0,0 +1,271 @@
|
|||
'use strict'
|
||||
|
||||
const BrowserResult = require('./browser_result')
|
||||
const helper = require('./helper')
|
||||
const logger = require('./logger')
|
||||
|
||||
const CONNECTED = 'CONNECTED' // The browser is connected but not yet been commanded to execute tests.
|
||||
const CONFIGURING = 'CONFIGURING' // The browser has been told to execute tests; it is configuring before tests execution.
|
||||
const EXECUTING = 'EXECUTING' // The browser is executing the tests.
|
||||
const EXECUTING_DISCONNECTED = 'EXECUTING_DISCONNECTED' // The browser is executing the tests, but temporarily disconnect (waiting for socket reconnecting).
|
||||
const DISCONNECTED = 'DISCONNECTED' // The browser got completely disconnected (e.g. browser crash) and can be only restored with a restart of execution.
|
||||
|
||||
class Browser {
|
||||
constructor (id, fullName, collection, emitter, socket, timer, disconnectDelay,
|
||||
noActivityTimeout, singleRun, clientConfig) {
|
||||
this.id = id
|
||||
this.fullName = fullName
|
||||
this.name = helper.browserFullNameToShort(fullName)
|
||||
this.lastResult = new BrowserResult()
|
||||
this.disconnectsCount = 0
|
||||
this.activeSockets = [socket]
|
||||
this.noActivityTimeout = noActivityTimeout
|
||||
this.singleRun = singleRun
|
||||
this.clientConfig = clientConfig
|
||||
this.collection = collection
|
||||
this.emitter = emitter
|
||||
this.socket = socket
|
||||
this.timer = timer
|
||||
this.disconnectDelay = disconnectDelay
|
||||
|
||||
this.log = logger.create(this.name)
|
||||
|
||||
this.noActivityTimeoutId = null
|
||||
this.pendingDisconnect = null
|
||||
this.setState(CONNECTED)
|
||||
}
|
||||
|
||||
init () {
|
||||
this.log.info(`Connected on socket ${this.socket.id} with id ${this.id}`)
|
||||
|
||||
this.bindSocketEvents(this.socket)
|
||||
this.collection.add(this)
|
||||
this.emitter.emit('browser_register', this)
|
||||
}
|
||||
|
||||
setState (toState) {
|
||||
this.log.debug(`${this.state} -> ${toState}`)
|
||||
this.state = toState
|
||||
}
|
||||
|
||||
onKarmaError (error) {
|
||||
if (this.isNotConnected()) {
|
||||
this.lastResult.error = true
|
||||
}
|
||||
this.emitter.emit('browser_error', this, error)
|
||||
this.refreshNoActivityTimeout()
|
||||
}
|
||||
|
||||
onInfo (info) {
|
||||
if (helper.isDefined(info.dump)) {
|
||||
this.emitter.emit('browser_log', this, info.dump, 'dump')
|
||||
}
|
||||
|
||||
if (helper.isDefined(info.log)) {
|
||||
this.emitter.emit('browser_log', this, info.log, info.type)
|
||||
} else if (helper.isDefined(info.total)) {
|
||||
if (this.state === EXECUTING) {
|
||||
this.lastResult.total = info.total
|
||||
}
|
||||
} else if (!helper.isDefined(info.dump)) {
|
||||
this.emitter.emit('browser_info', this, info)
|
||||
}
|
||||
|
||||
this.refreshNoActivityTimeout()
|
||||
}
|
||||
|
||||
onStart (info) {
|
||||
if (info.total === null) {
|
||||
this.log.warn('Adapter did not report total number of specs.')
|
||||
}
|
||||
|
||||
this.lastResult = new BrowserResult(info.total)
|
||||
this.setState(EXECUTING)
|
||||
this.emitter.emit('browser_start', this, info)
|
||||
this.refreshNoActivityTimeout()
|
||||
}
|
||||
|
||||
onComplete (result) {
|
||||
if (this.isNotConnected()) {
|
||||
this.setState(CONNECTED)
|
||||
this.lastResult.totalTimeEnd()
|
||||
|
||||
this.emitter.emit('browsers_change', this.collection)
|
||||
this.emitter.emit('browser_complete', this, result)
|
||||
|
||||
this.clearNoActivityTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
onSocketDisconnect (reason, disconnectedSocket) {
|
||||
helper.arrayRemove(this.activeSockets, disconnectedSocket)
|
||||
if (this.activeSockets.length) {
|
||||
this.log.debug(`Disconnected ${disconnectedSocket.id}, still have ${this.getActiveSocketsIds()}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isConnected()) {
|
||||
this.disconnect(`Client disconnected from CONNECTED state (${reason})`)
|
||||
} else if ([CONFIGURING, EXECUTING].includes(this.state)) {
|
||||
this.log.debug(`Disconnected during run, waiting ${this.disconnectDelay}ms for reconnecting.`)
|
||||
this.setState(EXECUTING_DISCONNECTED)
|
||||
|
||||
this.pendingDisconnect = this.timer.setTimeout(() => {
|
||||
this.lastResult.totalTimeEnd()
|
||||
this.lastResult.disconnected = true
|
||||
this.disconnect(`reconnect failed before timeout of ${this.disconnectDelay}ms (${reason})`)
|
||||
this.emitter.emit('browser_complete', this)
|
||||
}, this.disconnectDelay)
|
||||
|
||||
this.clearNoActivityTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
reconnect (newSocket, clientSaysReconnect) {
|
||||
if (!clientSaysReconnect || this.state === DISCONNECTED) {
|
||||
this.log.info(`Disconnected browser returned on socket ${newSocket.id} with id ${this.id}.`)
|
||||
this.setState(CONNECTED)
|
||||
|
||||
// The disconnected browser is already part of the collection.
|
||||
// Update the collection view in the UI (header on client.html)
|
||||
this.emitter.emit('browsers_change', this.collection)
|
||||
// Notify the launcher
|
||||
this.emitter.emit('browser_register', this)
|
||||
// Execute tests if configured to do so.
|
||||
if (this.singleRun) {
|
||||
this.execute()
|
||||
}
|
||||
} else if (this.state === EXECUTING_DISCONNECTED) {
|
||||
this.log.debug('Lost socket connection, but browser continued to execute. Reconnected ' +
|
||||
`on socket ${newSocket.id}.`)
|
||||
this.setState(EXECUTING)
|
||||
} else if ([CONNECTED, CONFIGURING, EXECUTING].includes(this.state)) {
|
||||
this.log.debug(`Rebinding to new socket ${newSocket.id} (already have ` +
|
||||
`${this.getActiveSocketsIds()})`)
|
||||
}
|
||||
|
||||
if (!this.activeSockets.some((s) => s.id === newSocket.id)) {
|
||||
this.activeSockets.push(newSocket)
|
||||
this.bindSocketEvents(newSocket)
|
||||
}
|
||||
|
||||
if (this.pendingDisconnect) {
|
||||
this.timer.clearTimeout(this.pendingDisconnect)
|
||||
}
|
||||
|
||||
this.refreshNoActivityTimeout()
|
||||
}
|
||||
|
||||
onResult (result) {
|
||||
if (Array.isArray(result)) {
|
||||
result.forEach(this.onResult, this)
|
||||
} else if (this.isNotConnected()) {
|
||||
this.lastResult.add(result)
|
||||
this.emitter.emit('spec_complete', this, result)
|
||||
}
|
||||
this.refreshNoActivityTimeout()
|
||||
}
|
||||
|
||||
execute () {
|
||||
this.activeSockets.forEach((socket) => socket.emit('execute', this.clientConfig))
|
||||
this.setState(CONFIGURING)
|
||||
this.refreshNoActivityTimeout()
|
||||
}
|
||||
|
||||
getActiveSocketsIds () {
|
||||
return this.activeSockets.map((s) => s.id).join(', ')
|
||||
}
|
||||
|
||||
disconnect (reason) {
|
||||
this.log.warn(`Disconnected (${this.disconnectsCount} times) ${reason || ''}`)
|
||||
this.disconnectsCount++
|
||||
this.emitter.emit('browser_error', this, `Disconnected ${reason || ''}`)
|
||||
this.remove()
|
||||
}
|
||||
|
||||
remove () {
|
||||
this.setState(DISCONNECTED)
|
||||
this.collection.remove(this)
|
||||
}
|
||||
|
||||
refreshNoActivityTimeout () {
|
||||
if (this.noActivityTimeout) {
|
||||
this.clearNoActivityTimeout()
|
||||
|
||||
this.noActivityTimeoutId = this.timer.setTimeout(() => {
|
||||
this.lastResult.totalTimeEnd()
|
||||
this.lastResult.disconnected = true
|
||||
this.disconnect(`, because no message in ${this.noActivityTimeout} ms.`)
|
||||
this.emitter.emit('browser_complete', this)
|
||||
}, this.noActivityTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
clearNoActivityTimeout () {
|
||||
if (this.noActivityTimeout && this.noActivityTimeoutId) {
|
||||
this.timer.clearTimeout(this.noActivityTimeoutId)
|
||||
this.noActivityTimeoutId = null
|
||||
}
|
||||
}
|
||||
|
||||
bindSocketEvents (socket) {
|
||||
// TODO: check which of these events are actually emitted by socket
|
||||
socket.on('disconnect', (reason) => this.onSocketDisconnect(reason, socket))
|
||||
socket.on('start', (info) => this.onStart(info))
|
||||
socket.on('karma_error', (error) => this.onKarmaError(error))
|
||||
socket.on('complete', (result) => this.onComplete(result))
|
||||
socket.on('info', (info) => this.onInfo(info))
|
||||
socket.on('result', (result) => this.onResult(result))
|
||||
}
|
||||
|
||||
isConnected () {
|
||||
return this.state === CONNECTED
|
||||
}
|
||||
|
||||
isNotConnected () {
|
||||
return !this.isConnected()
|
||||
}
|
||||
|
||||
serialize () {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
isConnected: this.state === CONNECTED
|
||||
}
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.name
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
id: this.id,
|
||||
fullName: this.fullName,
|
||||
name: this.name,
|
||||
state: this.state,
|
||||
lastResult: this.lastResult,
|
||||
disconnectsCount: this.disconnectsCount,
|
||||
noActivityTimeout: this.noActivityTimeout,
|
||||
disconnectDelay: this.disconnectDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Browser.factory = function (
|
||||
id, fullName, /* capturedBrowsers */ collection, emitter, socket, timer,
|
||||
/* config.browserDisconnectTimeout */ disconnectDelay,
|
||||
/* config.browserNoActivityTimeout */ noActivityTimeout,
|
||||
/* config.singleRun */ singleRun,
|
||||
/* config.client */ clientConfig) {
|
||||
return new Browser(id, fullName, collection, emitter, socket, timer,
|
||||
disconnectDelay, noActivityTimeout, singleRun, clientConfig)
|
||||
}
|
||||
|
||||
Browser.STATE_CONNECTED = CONNECTED
|
||||
Browser.STATE_CONFIGURING = CONFIGURING
|
||||
Browser.STATE_EXECUTING = EXECUTING
|
||||
Browser.STATE_EXECUTING_DISCONNECTED = EXECUTING_DISCONNECTED
|
||||
Browser.STATE_DISCONNECTED = DISCONNECTED
|
||||
|
||||
module.exports = Browser
|
103
my-app/node_modules/karma/lib/browser_collection.js
generated
vendored
Executable file
103
my-app/node_modules/karma/lib/browser_collection.js
generated
vendored
Executable file
|
@ -0,0 +1,103 @@
|
|||
'use strict'
|
||||
|
||||
const BrowserResult = require('./browser_result')
|
||||
const helper = require('./helper')
|
||||
|
||||
class BrowserCollection {
|
||||
constructor (emitter, browsers = []) {
|
||||
this.browsers = browsers
|
||||
this.emitter = emitter
|
||||
}
|
||||
|
||||
add (browser) {
|
||||
this.browsers.push(browser)
|
||||
this.emitter.emit('browsers_change', this)
|
||||
}
|
||||
|
||||
remove (browser) {
|
||||
if (helper.arrayRemove(this.browsers, browser)) {
|
||||
this.emitter.emit('browsers_change', this)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
getById (browserId) {
|
||||
return this.browsers.find((browser) => browser.id === browserId) || null
|
||||
}
|
||||
|
||||
getNonReady () {
|
||||
return this.browsers.filter((browser) => !browser.isConnected())
|
||||
}
|
||||
|
||||
areAllReady () {
|
||||
return this.browsers.every((browser) => browser.isConnected())
|
||||
}
|
||||
|
||||
serialize () {
|
||||
return this.browsers.map((browser) => browser.serialize())
|
||||
}
|
||||
|
||||
calculateExitCode (results, singleRunBrowserNotCaptured, config) {
|
||||
config = config || {}
|
||||
if (results.disconnected || singleRunBrowserNotCaptured) {
|
||||
return 1
|
||||
}
|
||||
if (results.skipped && config.failOnSkippedTests) {
|
||||
return 1
|
||||
}
|
||||
if (results.success + results.failed === 0 && !!config.failOnEmptyTestSuite) {
|
||||
return 1
|
||||
}
|
||||
if (results.error) {
|
||||
return 1
|
||||
}
|
||||
if (config.failOnFailingTestSuite === false) {
|
||||
return 0 // Tests executed without infrastructure error, exit with 0 independent of test status.
|
||||
}
|
||||
return results.failed ? 1 : 0
|
||||
}
|
||||
|
||||
getResults (singleRunBrowserNotCaptured, config) {
|
||||
const results = { success: 0, failed: 0, skipped: 0, error: false, disconnected: false, exitCode: 0 }
|
||||
this.browsers.forEach((browser) => {
|
||||
results.success += browser.lastResult.success
|
||||
results.failed += browser.lastResult.failed
|
||||
results.skipped += browser.lastResult.skipped
|
||||
results.error = results.error || browser.lastResult.error
|
||||
results.disconnected = results.disconnected || browser.lastResult.disconnected
|
||||
})
|
||||
|
||||
results.exitCode = this.calculateExitCode(results, singleRunBrowserNotCaptured, config)
|
||||
return results
|
||||
}
|
||||
|
||||
clearResults () {
|
||||
this.browsers.forEach((browser) => {
|
||||
browser.lastResult = new BrowserResult()
|
||||
})
|
||||
}
|
||||
|
||||
clone () {
|
||||
return new BrowserCollection(this.emitter, this.browsers.slice())
|
||||
}
|
||||
|
||||
// Array APIs
|
||||
map (callback, context) {
|
||||
return this.browsers.map(callback, context)
|
||||
}
|
||||
|
||||
forEach (callback, context) {
|
||||
return this.browsers.forEach(callback, context)
|
||||
}
|
||||
|
||||
get length () {
|
||||
return this.browsers.length
|
||||
}
|
||||
}
|
||||
|
||||
BrowserCollection.factory = function (emitter) {
|
||||
return new BrowserCollection(emitter)
|
||||
}
|
||||
|
||||
module.exports = BrowserCollection
|
30
my-app/node_modules/karma/lib/browser_result.js
generated
vendored
Executable file
30
my-app/node_modules/karma/lib/browser_result.js
generated
vendored
Executable file
|
@ -0,0 +1,30 @@
|
|||
'use strict'
|
||||
|
||||
class BrowserResult {
|
||||
constructor (total = 0) {
|
||||
this.startTime = Date.now()
|
||||
|
||||
this.total = total
|
||||
this.skipped = this.failed = this.success = 0
|
||||
this.netTime = this.totalTime = 0
|
||||
this.disconnected = this.error = false
|
||||
}
|
||||
|
||||
totalTimeEnd () {
|
||||
this.totalTime = Date.now() - this.startTime
|
||||
}
|
||||
|
||||
add (result) {
|
||||
if (result.skipped) {
|
||||
this.skipped++
|
||||
} else if (result.success) {
|
||||
this.success++
|
||||
} else {
|
||||
this.failed++
|
||||
}
|
||||
|
||||
this.netTime += result.time
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BrowserResult
|
331
my-app/node_modules/karma/lib/cli.js
generated
vendored
Executable file
331
my-app/node_modules/karma/lib/cli.js
generated
vendored
Executable file
|
@ -0,0 +1,331 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const yargs = require('yargs')
|
||||
const fs = require('graceful-fs')
|
||||
|
||||
const Server = require('./server')
|
||||
const helper = require('./helper')
|
||||
const constant = require('./constants')
|
||||
const cfg = require('./config')
|
||||
|
||||
function processArgs (argv, options, fs, path) {
|
||||
Object.getOwnPropertyNames(argv).forEach(function (name) {
|
||||
let argumentValue = argv[name]
|
||||
if (name !== '_' && name !== '$0') {
|
||||
if (Array.isArray(argumentValue)) {
|
||||
argumentValue = argumentValue.pop() // If the same argument is defined multiple times, override.
|
||||
}
|
||||
options[helper.dashToCamel(name)] = argumentValue
|
||||
}
|
||||
})
|
||||
|
||||
if (helper.isString(options.autoWatch)) {
|
||||
options.autoWatch = options.autoWatch === 'true'
|
||||
}
|
||||
|
||||
if (helper.isString(options.colors)) {
|
||||
options.colors = options.colors === 'true'
|
||||
}
|
||||
|
||||
if (helper.isString(options.failOnEmptyTestSuite)) {
|
||||
options.failOnEmptyTestSuite = options.failOnEmptyTestSuite === 'true'
|
||||
}
|
||||
|
||||
if (helper.isString(options.failOnFailingTestSuite)) {
|
||||
options.failOnFailingTestSuite = options.failOnFailingTestSuite === 'true'
|
||||
}
|
||||
|
||||
if (helper.isString(options.formatError)) {
|
||||
let required
|
||||
try {
|
||||
required = require(options.formatError)
|
||||
} catch (err) {
|
||||
console.error('Could not require formatError: ' + options.formatError, err)
|
||||
}
|
||||
// support exports.formatError and module.exports = function
|
||||
options.formatError = required.formatError || required
|
||||
if (!helper.isFunction(options.formatError)) {
|
||||
console.error(`Format error must be a function, got: ${typeof options.formatError}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if (helper.isString(options.logLevel)) {
|
||||
const logConstant = constant['LOG_' + options.logLevel.toUpperCase()]
|
||||
if (helper.isDefined(logConstant)) {
|
||||
options.logLevel = logConstant
|
||||
} else {
|
||||
console.error('Log level must be one of disable, error, warn, info, or debug.')
|
||||
process.exit(1)
|
||||
}
|
||||
} else if (helper.isDefined(options.logLevel)) {
|
||||
console.error('Log level must be one of disable, error, warn, info, or debug.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (helper.isString(options.singleRun)) {
|
||||
options.singleRun = options.singleRun === 'true'
|
||||
}
|
||||
|
||||
if (helper.isString(options.browsers)) {
|
||||
options.browsers = options.browsers.split(',')
|
||||
}
|
||||
|
||||
if (options.reportSlowerThan === false) {
|
||||
options.reportSlowerThan = 0
|
||||
}
|
||||
|
||||
if (helper.isString(options.reporters)) {
|
||||
options.reporters = options.reporters.split(',')
|
||||
}
|
||||
|
||||
if (helper.isString(options.removedFiles)) {
|
||||
options.removedFiles = options.removedFiles.split(',')
|
||||
}
|
||||
|
||||
if (helper.isString(options.addedFiles)) {
|
||||
options.addedFiles = options.addedFiles.split(',')
|
||||
}
|
||||
|
||||
if (helper.isString(options.changedFiles)) {
|
||||
options.changedFiles = options.changedFiles.split(',')
|
||||
}
|
||||
|
||||
if (helper.isString(options.refresh)) {
|
||||
options.refresh = options.refresh === 'true'
|
||||
}
|
||||
|
||||
let configFile = argv.configFile
|
||||
|
||||
if (!configFile) {
|
||||
// default config file (if exists)
|
||||
if (fs.existsSync('./karma.conf.js')) {
|
||||
configFile = './karma.conf.js'
|
||||
} else if (fs.existsSync('./karma.conf.coffee')) {
|
||||
configFile = './karma.conf.coffee'
|
||||
} else if (fs.existsSync('./karma.conf.ts')) {
|
||||
configFile = './karma.conf.ts'
|
||||
} else if (fs.existsSync('./.config/karma.conf.js')) {
|
||||
configFile = './.config/karma.conf.js'
|
||||
} else if (fs.existsSync('./.config/karma.conf.coffee')) {
|
||||
configFile = './.config/karma.conf.coffee'
|
||||
} else if (fs.existsSync('./.config/karma.conf.ts')) {
|
||||
configFile = './.config/karma.conf.ts'
|
||||
}
|
||||
}
|
||||
|
||||
options.configFile = configFile ? path.resolve(configFile) : null
|
||||
|
||||
if (options.cmd === 'run') {
|
||||
options.clientArgs = parseClientArgs(process.argv)
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
function parseClientArgs (argv) {
|
||||
// extract any args after '--' as clientArgs
|
||||
let clientArgs = []
|
||||
argv = argv.slice(2)
|
||||
const idx = argv.indexOf('--')
|
||||
if (idx !== -1) {
|
||||
clientArgs = argv.slice(idx + 1)
|
||||
}
|
||||
return clientArgs
|
||||
}
|
||||
|
||||
// return only args that occur before `--`
|
||||
function argsBeforeDoubleDash (argv) {
|
||||
const idx = argv.indexOf('--')
|
||||
|
||||
return idx === -1 ? argv : argv.slice(0, idx)
|
||||
}
|
||||
|
||||
function describeRoot () {
|
||||
return yargs
|
||||
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
|
||||
'Run --help with particular command to see its description and available options.\n\n' +
|
||||
'Usage:\n' +
|
||||
' $0 <command>')
|
||||
.command('init [configFile]', 'Initialize a config file.', describeInit)
|
||||
.command('start [configFile]', 'Start the server / do a single run.', describeStart)
|
||||
.command('run [configFile]', 'Trigger a test run.', describeRun)
|
||||
.command('stop [configFile]', 'Stop the server.', describeStop)
|
||||
.command('completion', 'Shell completion for karma.', describeCompletion)
|
||||
.demandCommand(1, 'Command not specified.')
|
||||
.strictCommands()
|
||||
.describe('help', 'Print usage and options.')
|
||||
.describe('version', 'Print current version.')
|
||||
}
|
||||
|
||||
function describeInit (yargs) {
|
||||
yargs
|
||||
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
|
||||
'INIT - Initialize a config file.\n\n' +
|
||||
'Usage:\n' +
|
||||
' $0 init [configFile]')
|
||||
.strictCommands(false)
|
||||
.version(false)
|
||||
.positional('configFile', {
|
||||
describe: 'Name of the generated Karma configuration file',
|
||||
type: 'string'
|
||||
})
|
||||
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
|
||||
.describe('colors', 'Use colors when reporting and printing logs.')
|
||||
.describe('no-colors', 'Do not use colors when reporting or printing logs.')
|
||||
}
|
||||
|
||||
function describeStart (yargs) {
|
||||
yargs
|
||||
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
|
||||
'START - Start the server / do a single run.\n\n' +
|
||||
'Usage:\n' +
|
||||
' $0 start [configFile]')
|
||||
.strictCommands(false)
|
||||
.version(false)
|
||||
.positional('configFile', {
|
||||
describe: 'Path to the Karma configuration file',
|
||||
type: 'string'
|
||||
})
|
||||
.describe('port', '<integer> Port where the server is running.')
|
||||
.describe('auto-watch', 'Auto watch source files and run on change.')
|
||||
.describe('detached', 'Detach the server.')
|
||||
.describe('no-auto-watch', 'Do not watch source files.')
|
||||
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
|
||||
.describe('colors', 'Use colors when reporting and printing logs.')
|
||||
.describe('no-colors', 'Do not use colors when reporting or printing logs.')
|
||||
.describe('reporters', 'List of reporters (available: dots, progress, junit, growl, coverage).')
|
||||
.describe('browsers', 'List of browsers to start (eg. --browsers Chrome,ChromeCanary,Firefox).')
|
||||
.describe('capture-timeout', '<integer> Kill browser if does not capture in given time [ms].')
|
||||
.describe('single-run', 'Run the test when browsers captured and exit.')
|
||||
.describe('no-single-run', 'Disable single-run.')
|
||||
.describe('report-slower-than', '<integer> Report tests that are slower than given time [ms].')
|
||||
.describe('fail-on-empty-test-suite', 'Fail on empty test suite.')
|
||||
.describe('no-fail-on-empty-test-suite', 'Do not fail on empty test suite.')
|
||||
.describe('fail-on-failing-test-suite', 'Fail on failing test suite.')
|
||||
.describe('no-fail-on-failing-test-suite', 'Do not fail on failing test suite.')
|
||||
.option('format-error', {
|
||||
describe: 'A path to a file that exports the format function.',
|
||||
type: 'string'
|
||||
})
|
||||
}
|
||||
|
||||
function describeRun (yargs) {
|
||||
yargs
|
||||
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
|
||||
'RUN - Run the tests (requires running server).\n\n' +
|
||||
'Usage:\n' +
|
||||
' $0 run [configFile] [-- <clientArgs>]')
|
||||
.strictCommands(false)
|
||||
.version(false)
|
||||
.positional('configFile', {
|
||||
describe: 'Path to the Karma configuration file',
|
||||
type: 'string'
|
||||
})
|
||||
.describe('port', '<integer> Port where the server is listening.')
|
||||
.describe('no-refresh', 'Do not re-glob all the patterns.')
|
||||
.describe('fail-on-empty-test-suite', 'Fail on empty test suite.')
|
||||
.describe('no-fail-on-empty-test-suite', 'Do not fail on empty test suite.')
|
||||
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
|
||||
.describe('colors', 'Use colors when reporting and printing logs.')
|
||||
.describe('no-colors', 'Do not use colors when reporting or printing logs.')
|
||||
.option('removed-files', {
|
||||
describe: 'Comma-separated paths to removed files. Useful when automatic file watching is disabled.',
|
||||
type: 'string'
|
||||
})
|
||||
.option('changed-files', {
|
||||
describe: 'Comma-separated paths to changed files. Useful when automatic file watching is disabled.',
|
||||
type: 'string'
|
||||
})
|
||||
.option('added-files', {
|
||||
describe: 'Comma-separated paths to added files. Useful when automatic file watching is disabled.',
|
||||
type: 'string'
|
||||
})
|
||||
}
|
||||
|
||||
function describeStop (yargs) {
|
||||
yargs
|
||||
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
|
||||
'STOP - Stop the server (requires running server).\n\n' +
|
||||
'Usage:\n' +
|
||||
' $0 stop [configFile]')
|
||||
.strictCommands(false)
|
||||
.version(false)
|
||||
.positional('configFile', {
|
||||
describe: 'Path to the Karma configuration file',
|
||||
type: 'string'
|
||||
})
|
||||
.describe('port', '<integer> Port where the server is listening.')
|
||||
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
|
||||
}
|
||||
|
||||
function describeCompletion (yargs) {
|
||||
yargs
|
||||
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
|
||||
'COMPLETION - Bash/ZSH completion for karma.\n\n' +
|
||||
'Installation:\n' +
|
||||
' $0 completion >> ~/.bashrc')
|
||||
.version(false)
|
||||
}
|
||||
|
||||
function printRunnerProgress (data) {
|
||||
process.stdout.write(data)
|
||||
}
|
||||
|
||||
exports.process = () => {
|
||||
const argv = describeRoot().parse(argsBeforeDoubleDash(process.argv.slice(2)))
|
||||
return processArgs(argv, { cmd: argv._.shift() }, fs, path)
|
||||
}
|
||||
|
||||
exports.run = async () => {
|
||||
const cliOptions = exports.process()
|
||||
const cmd = cliOptions.cmd // prevent config from changing the command
|
||||
const cmdNeedsConfig = cmd === 'start' || cmd === 'run' || cmd === 'stop'
|
||||
if (cmdNeedsConfig) {
|
||||
let config
|
||||
try {
|
||||
config = await cfg.parseConfig(
|
||||
cliOptions.configFile,
|
||||
cliOptions,
|
||||
{
|
||||
promiseConfig: true,
|
||||
throwErrors: true
|
||||
}
|
||||
)
|
||||
} catch (karmaConfigException) {
|
||||
// The reject reason/exception isn't used to log a message since
|
||||
// parseConfig already calls a configured logger method with an almost
|
||||
// identical message.
|
||||
|
||||
// The `run` function is a private application, not a public API. We don't
|
||||
// need to worry about process.exit vs throw vs promise rejection here.
|
||||
process.exit(1)
|
||||
}
|
||||
switch (cmd) {
|
||||
case 'start': {
|
||||
const server = new Server(config)
|
||||
await server.start()
|
||||
return server
|
||||
}
|
||||
case 'run':
|
||||
return require('./runner')
|
||||
.run(config)
|
||||
.on('progress', printRunnerProgress)
|
||||
case 'stop':
|
||||
return require('./stopper').stop(config)
|
||||
}
|
||||
} else {
|
||||
switch (cmd) {
|
||||
case 'init':
|
||||
return require('./init').init(cliOptions)
|
||||
case 'completion':
|
||||
return require('./completion').completion(cliOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// just for testing
|
||||
exports.processArgs = processArgs
|
||||
exports.parseClientArgs = parseClientArgs
|
||||
exports.argsBeforeDoubleDash = argsBeforeDoubleDash
|
126
my-app/node_modules/karma/lib/completion.js
generated
vendored
Executable file
126
my-app/node_modules/karma/lib/completion.js
generated
vendored
Executable file
|
@ -0,0 +1,126 @@
|
|||
'use strict'
|
||||
|
||||
const glob = require('glob')
|
||||
|
||||
const CUSTOM = ['']
|
||||
const BOOLEAN = false
|
||||
|
||||
const options = {
|
||||
start: {
|
||||
'--port': CUSTOM,
|
||||
'--auto-watch': BOOLEAN,
|
||||
'--no-auto-watch': BOOLEAN,
|
||||
'--log-level': ['disable', 'debug', 'info', 'warn', 'error'],
|
||||
'--colors': BOOLEAN,
|
||||
'--no-colors': BOOLEAN,
|
||||
'--reporters': ['dots', 'progress'],
|
||||
'--no-reporters': BOOLEAN,
|
||||
'--browsers': ['Chrome', 'ChromeHeadless', 'ChromeCanary', 'Firefox', 'PhantomJS', 'Safari', 'Opera'],
|
||||
'--no-browsers': BOOLEAN,
|
||||
'--single-run': BOOLEAN,
|
||||
'--no-single-run': BOOLEAN,
|
||||
'--help': BOOLEAN
|
||||
},
|
||||
init: {
|
||||
'--log-level': ['disable', 'debug', 'info', 'warn', 'error'],
|
||||
'--colors': BOOLEAN,
|
||||
'--no-colors': BOOLEAN,
|
||||
'--help': BOOLEAN
|
||||
},
|
||||
run: {
|
||||
'--no-refresh': BOOLEAN,
|
||||
'--port': CUSTOM,
|
||||
'--help': BOOLEAN
|
||||
}
|
||||
}
|
||||
|
||||
function opositeWord (word) {
|
||||
if (word.startsWith('-')) {
|
||||
return word.startsWith('--no-') ? `--${word.slice(5)}` : `--no-${word.slice(2)}`
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function sendCompletion (possibleWords, env) {
|
||||
const regexp = new RegExp('^' + env.last)
|
||||
possibleWords
|
||||
.filter((word) => regexp.test(word) && !env.words.includes(word) && !env.words.includes(opositeWord(word)))
|
||||
.forEach((word) => {
|
||||
console.log(word)
|
||||
})
|
||||
}
|
||||
|
||||
function sendCompletionFiles (env) {
|
||||
glob(env.last + '*', { mark: true, nocase: true }, (err, files) => {
|
||||
if (err) return console.error(err)
|
||||
|
||||
if (files.length === 1 && files[0].endsWith('/')) {
|
||||
sendCompletionFiles({ last: files[0] })
|
||||
} else {
|
||||
console.log(files.join('\n'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function complete (env) {
|
||||
if (env.count === 1) {
|
||||
return sendCompletion(env.words[0].startsWith('-') ? ['--help', '--version'] : Object.keys(options), env)
|
||||
} else if (env.count === 2 && !env.words[1].startsWith('-')) {
|
||||
return sendCompletionFiles(env)
|
||||
}
|
||||
|
||||
const cmdOptions = options[env.words[0]]
|
||||
|
||||
if (cmdOptions) {
|
||||
if (cmdOptions[env.prev] === CUSTOM && env.last) {
|
||||
console.log(env.last)
|
||||
} else {
|
||||
return sendCompletion(cmdOptions[env.prev] || Object.keys(cmdOptions), env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function completion () {
|
||||
if (process.argv[3] === '--') {
|
||||
return complete({
|
||||
words: process.argv.slice(5),
|
||||
count: parseInt(process.env.COMP_CWORD, 10),
|
||||
last: process.argv[process.argv.length - 1],
|
||||
prev: process.argv[process.argv.length - 2]
|
||||
})
|
||||
}
|
||||
|
||||
// just print out the karma-completion.sh
|
||||
const fs = require('graceful-fs')
|
||||
const path = require('path')
|
||||
|
||||
fs.readFile(path.resolve(__dirname, '../scripts/karma-completion.sh'), 'utf8', function (err, data) {
|
||||
if (err) return console.error(err)
|
||||
|
||||
process.stdout.write(data)
|
||||
process.stdout.on('error', function (error) {
|
||||
// Darwin is a real dick sometimes.
|
||||
//
|
||||
// This is necessary because the "source" or "." program in
|
||||
// bash on OS X closes its file argument before reading
|
||||
// from it, meaning that you get exactly 1 write, which will
|
||||
// work most of the time, and will always raise an EPIPE.
|
||||
//
|
||||
// Really, one should not be tossing away EPIPE errors, or any
|
||||
// errors, so casually. But, without this, `. <(karma completion)`
|
||||
// can never ever work on OS X.
|
||||
if (error.errno === 'EPIPE') {
|
||||
error = null
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
exports.completion = completion
|
||||
|
||||
// for testing
|
||||
exports.opositeWord = opositeWord
|
||||
exports.sendCompletion = sendCompletion
|
||||
exports.complete = complete
|
544
my-app/node_modules/karma/lib/config.js
generated
vendored
Executable file
544
my-app/node_modules/karma/lib/config.js
generated
vendored
Executable file
|
@ -0,0 +1,544 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
const logger = require('./logger')
|
||||
const log = logger.create('config')
|
||||
const helper = require('./helper')
|
||||
const constant = require('./constants')
|
||||
|
||||
const _ = require('lodash')
|
||||
|
||||
let COFFEE_SCRIPT_AVAILABLE = false
|
||||
let LIVE_SCRIPT_AVAILABLE = false
|
||||
let TYPE_SCRIPT_AVAILABLE = false
|
||||
|
||||
try {
|
||||
require('coffeescript').register()
|
||||
COFFEE_SCRIPT_AVAILABLE = true
|
||||
} catch {}
|
||||
|
||||
// LiveScript is required here to enable config files written in LiveScript.
|
||||
// It's not directly used in this file.
|
||||
try {
|
||||
require('LiveScript')
|
||||
LIVE_SCRIPT_AVAILABLE = true
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
require('ts-node')
|
||||
TYPE_SCRIPT_AVAILABLE = true
|
||||
} catch {}
|
||||
|
||||
class Pattern {
|
||||
constructor (pattern, served, included, watched, nocache, type, isBinary, integrity) {
|
||||
this.pattern = pattern
|
||||
this.served = helper.isDefined(served) ? served : true
|
||||
this.included = helper.isDefined(included) ? included : true
|
||||
this.watched = helper.isDefined(watched) ? watched : true
|
||||
this.nocache = helper.isDefined(nocache) ? nocache : false
|
||||
this.weight = helper.mmPatternWeight(pattern)
|
||||
this.type = type
|
||||
this.isBinary = isBinary
|
||||
this.integrity = integrity
|
||||
}
|
||||
|
||||
compare (other) {
|
||||
return helper.mmComparePatternWeights(this.weight, other.weight)
|
||||
}
|
||||
}
|
||||
|
||||
class UrlPattern extends Pattern {
|
||||
constructor (url, type, integrity) {
|
||||
super(url, false, true, false, false, type, undefined, integrity)
|
||||
}
|
||||
}
|
||||
|
||||
function createPatternObject (pattern) {
|
||||
if (pattern && helper.isString(pattern)) {
|
||||
return helper.isUrlAbsolute(pattern)
|
||||
? new UrlPattern(pattern)
|
||||
: new Pattern(pattern)
|
||||
} else if (helper.isObject(pattern) && pattern.pattern && helper.isString(pattern.pattern)) {
|
||||
return helper.isUrlAbsolute(pattern.pattern)
|
||||
? new UrlPattern(pattern.pattern, pattern.type, pattern.integrity)
|
||||
: new Pattern(pattern.pattern, pattern.served, pattern.included, pattern.watched, pattern.nocache, pattern.type)
|
||||
} else {
|
||||
log.warn(`Invalid pattern ${pattern}!\n\tExpected string or object with "pattern" property.`)
|
||||
return new Pattern(null, false, false, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeUrl (url) {
|
||||
if (!url.startsWith('/')) {
|
||||
url = `/${url}`
|
||||
}
|
||||
|
||||
if (!url.endsWith('/')) {
|
||||
url = url + '/'
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
function normalizeUrlRoot (urlRoot) {
|
||||
const normalizedUrlRoot = normalizeUrl(urlRoot)
|
||||
|
||||
if (normalizedUrlRoot !== urlRoot) {
|
||||
log.warn(`urlRoot normalized to "${normalizedUrlRoot}"`)
|
||||
}
|
||||
|
||||
return normalizedUrlRoot
|
||||
}
|
||||
|
||||
function normalizeProxyPath (proxyPath) {
|
||||
const normalizedProxyPath = normalizeUrl(proxyPath)
|
||||
|
||||
if (normalizedProxyPath !== proxyPath) {
|
||||
log.warn(`proxyPath normalized to "${normalizedProxyPath}"`)
|
||||
}
|
||||
|
||||
return normalizedProxyPath
|
||||
}
|
||||
|
||||
function normalizeConfig (config, configFilePath) {
|
||||
function basePathResolve (relativePath) {
|
||||
if (helper.isUrlAbsolute(relativePath)) {
|
||||
return relativePath
|
||||
} else if (helper.isDefined(config.basePath) && helper.isDefined(relativePath)) {
|
||||
return path.resolve(config.basePath, relativePath)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function createPatternMapper (resolve) {
|
||||
return (objectPattern) => Object.assign(objectPattern, { pattern: resolve(objectPattern.pattern) })
|
||||
}
|
||||
|
||||
if (helper.isString(configFilePath)) {
|
||||
config.basePath = path.resolve(path.dirname(configFilePath), config.basePath) // resolve basePath
|
||||
config.exclude.push(configFilePath) // always ignore the config file itself
|
||||
} else {
|
||||
config.basePath = path.resolve(config.basePath || '.')
|
||||
}
|
||||
|
||||
config.files = config.files.map(createPatternObject).map(createPatternMapper(basePathResolve))
|
||||
config.exclude = config.exclude.map(basePathResolve)
|
||||
config.customContextFile = config.customContextFile && basePathResolve(config.customContextFile)
|
||||
config.customDebugFile = config.customDebugFile && basePathResolve(config.customDebugFile)
|
||||
config.customClientContextFile = config.customClientContextFile && basePathResolve(config.customClientContextFile)
|
||||
|
||||
// normalize paths on windows
|
||||
config.basePath = helper.normalizeWinPath(config.basePath)
|
||||
config.files = config.files.map(createPatternMapper(helper.normalizeWinPath))
|
||||
config.exclude = config.exclude.map(helper.normalizeWinPath)
|
||||
config.customContextFile = helper.normalizeWinPath(config.customContextFile)
|
||||
config.customDebugFile = helper.normalizeWinPath(config.customDebugFile)
|
||||
config.customClientContextFile = helper.normalizeWinPath(config.customClientContextFile)
|
||||
|
||||
// normalize urlRoot
|
||||
config.urlRoot = normalizeUrlRoot(config.urlRoot)
|
||||
|
||||
// normalize and default upstream proxy settings if given
|
||||
if (config.upstreamProxy) {
|
||||
const proxy = config.upstreamProxy
|
||||
proxy.path = helper.isDefined(proxy.path) ? normalizeProxyPath(proxy.path) : '/'
|
||||
proxy.hostname = helper.isDefined(proxy.hostname) ? proxy.hostname : 'localhost'
|
||||
proxy.port = helper.isDefined(proxy.port) ? proxy.port : 9875
|
||||
|
||||
// force protocol to end with ':'
|
||||
proxy.protocol = (proxy.protocol || 'http').split(':')[0] + ':'
|
||||
if (proxy.protocol.match(/https?:/) === null) {
|
||||
log.warn(`"${proxy.protocol}" is not a supported upstream proxy protocol, defaulting to "http:"`)
|
||||
proxy.protocol = 'http:'
|
||||
}
|
||||
}
|
||||
|
||||
// force protocol to end with ':'
|
||||
config.protocol = (config.protocol || 'http').split(':')[0] + ':'
|
||||
if (config.protocol.match(/https?:/) === null) {
|
||||
log.warn(`"${config.protocol}" is not a supported protocol, defaulting to "http:"`)
|
||||
config.protocol = 'http:'
|
||||
}
|
||||
|
||||
if (config.proxies && Object.prototype.hasOwnProperty.call(config.proxies, config.urlRoot)) {
|
||||
log.warn(`"${config.urlRoot}" is proxied, you should probably change urlRoot to avoid conflicts`)
|
||||
}
|
||||
|
||||
if (config.singleRun && config.autoWatch) {
|
||||
log.debug('autoWatch set to false, because of singleRun')
|
||||
config.autoWatch = false
|
||||
}
|
||||
|
||||
if (config.runInParent) {
|
||||
log.debug('useIframe set to false, because using runInParent')
|
||||
config.useIframe = false
|
||||
}
|
||||
|
||||
if (!config.singleRun && !config.useIframe && config.runInParent) {
|
||||
log.debug('singleRun set to true, because using runInParent')
|
||||
config.singleRun = true
|
||||
}
|
||||
|
||||
if (helper.isString(config.reporters)) {
|
||||
config.reporters = config.reporters.split(',')
|
||||
}
|
||||
|
||||
if (config.client && config.client.args) {
|
||||
assert(Array.isArray(config.client.args), 'Invalid configuration: client.args must be an array of strings')
|
||||
}
|
||||
|
||||
if (config.browsers) {
|
||||
assert(Array.isArray(config.browsers), 'Invalid configuration: browsers option must be an array')
|
||||
}
|
||||
|
||||
if (config.formatError) {
|
||||
assert(helper.isFunction(config.formatError), 'Invalid configuration: formatError option must be a function.')
|
||||
}
|
||||
|
||||
if (config.processKillTimeout) {
|
||||
assert(helper.isNumber(config.processKillTimeout), 'Invalid configuration: processKillTimeout option must be a number.')
|
||||
}
|
||||
|
||||
if (config.browserSocketTimeout) {
|
||||
assert(helper.isNumber(config.browserSocketTimeout), 'Invalid configuration: browserSocketTimeout option must be a number.')
|
||||
}
|
||||
|
||||
if (config.pingTimeout) {
|
||||
assert(helper.isNumber(config.pingTimeout), 'Invalid configuration: pingTimeout option must be a number.')
|
||||
}
|
||||
|
||||
const defaultClient = config.defaultClient || {}
|
||||
Object.keys(defaultClient).forEach(function (key) {
|
||||
const option = config.client[key]
|
||||
config.client[key] = helper.isDefined(option) ? option : defaultClient[key]
|
||||
})
|
||||
|
||||
// normalize preprocessors
|
||||
const preprocessors = config.preprocessors || {}
|
||||
const normalizedPreprocessors = config.preprocessors = Object.create(null)
|
||||
|
||||
Object.keys(preprocessors).forEach(function (pattern) {
|
||||
const normalizedPattern = helper.normalizeWinPath(basePathResolve(pattern))
|
||||
|
||||
normalizedPreprocessors[normalizedPattern] = helper.isString(preprocessors[pattern])
|
||||
? [preprocessors[pattern]] : preprocessors[pattern]
|
||||
})
|
||||
|
||||
// define custom launchers/preprocessors/reporters - create a new plugin
|
||||
const module = Object.create(null)
|
||||
let hasSomeInlinedPlugin = false
|
||||
const types = ['launcher', 'preprocessor', 'reporter']
|
||||
|
||||
types.forEach(function (type) {
|
||||
const definitions = config[`custom${helper.ucFirst(type)}s`] || {}
|
||||
|
||||
Object.keys(definitions).forEach(function (name) {
|
||||
const definition = definitions[name]
|
||||
|
||||
if (!helper.isObject(definition)) {
|
||||
return log.warn(`Can not define ${type} ${name}. Definition has to be an object.`)
|
||||
}
|
||||
|
||||
if (!helper.isString(definition.base)) {
|
||||
return log.warn(`Can not define ${type} ${name}. Missing base ${type}.`)
|
||||
}
|
||||
|
||||
const token = type + ':' + definition.base
|
||||
const locals = {
|
||||
args: ['value', definition]
|
||||
}
|
||||
|
||||
module[type + ':' + name] = ['factory', function (injector) {
|
||||
const plugin = injector.createChild([locals], [token]).get(token)
|
||||
if (type === 'launcher' && helper.isDefined(definition.displayName)) {
|
||||
plugin.displayName = definition.displayName
|
||||
}
|
||||
return plugin
|
||||
}]
|
||||
hasSomeInlinedPlugin = true
|
||||
})
|
||||
})
|
||||
|
||||
if (hasSomeInlinedPlugin) {
|
||||
config.plugins.push(module)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
class Config {
|
||||
constructor () {
|
||||
this.LOG_DISABLE = constant.LOG_DISABLE
|
||||
this.LOG_ERROR = constant.LOG_ERROR
|
||||
this.LOG_WARN = constant.LOG_WARN
|
||||
this.LOG_INFO = constant.LOG_INFO
|
||||
this.LOG_DEBUG = constant.LOG_DEBUG
|
||||
|
||||
// DEFAULT CONFIG
|
||||
this.frameworks = []
|
||||
this.protocol = 'http:'
|
||||
this.port = constant.DEFAULT_PORT
|
||||
this.listenAddress = constant.DEFAULT_LISTEN_ADDR
|
||||
this.hostname = constant.DEFAULT_HOSTNAME
|
||||
this.httpsServerConfig = {}
|
||||
this.basePath = ''
|
||||
this.files = []
|
||||
this.browserConsoleLogOptions = {
|
||||
level: 'debug',
|
||||
format: '%b %T: %m',
|
||||
terminal: true
|
||||
}
|
||||
this.customContextFile = null
|
||||
this.customDebugFile = null
|
||||
this.customClientContextFile = null
|
||||
this.exclude = []
|
||||
this.logLevel = constant.LOG_INFO
|
||||
this.colors = true
|
||||
this.autoWatch = true
|
||||
this.autoWatchBatchDelay = 250
|
||||
this.restartOnFileChange = false
|
||||
this.usePolling = process.platform === 'linux'
|
||||
this.reporters = ['progress']
|
||||
this.singleRun = false
|
||||
this.browsers = []
|
||||
this.captureTimeout = 60000
|
||||
this.pingTimeout = 5000
|
||||
this.proxies = {}
|
||||
this.proxyValidateSSL = true
|
||||
this.preprocessors = {}
|
||||
this.preprocessor_priority = {}
|
||||
this.urlRoot = '/'
|
||||
this.upstreamProxy = undefined
|
||||
this.reportSlowerThan = 0
|
||||
this.loggers = [constant.CONSOLE_APPENDER]
|
||||
this.transports = ['polling', 'websocket']
|
||||
this.forceJSONP = false
|
||||
this.plugins = ['karma-*']
|
||||
this.defaultClient = this.client = {
|
||||
args: [],
|
||||
useIframe: true,
|
||||
runInParent: false,
|
||||
captureConsole: true,
|
||||
clearContext: true,
|
||||
allowedReturnUrlPatterns: ['^https?://']
|
||||
}
|
||||
this.browserDisconnectTimeout = 2000
|
||||
this.browserDisconnectTolerance = 0
|
||||
this.browserNoActivityTimeout = 30000
|
||||
this.processKillTimeout = 2000
|
||||
this.concurrency = Infinity
|
||||
this.failOnEmptyTestSuite = true
|
||||
this.retryLimit = 2
|
||||
this.detached = false
|
||||
this.crossOriginAttribute = true
|
||||
this.browserSocketTimeout = 20000
|
||||
}
|
||||
|
||||
set (newConfig) {
|
||||
_.mergeWith(this, newConfig, (obj, src) => {
|
||||
// Overwrite arrays to keep consistent with #283
|
||||
if (Array.isArray(src)) {
|
||||
return src
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const CONFIG_SYNTAX_HELP = ' module.exports = function(config) {\n' +
|
||||
' config.set({\n' +
|
||||
' // your config\n' +
|
||||
' });\n' +
|
||||
' };\n'
|
||||
|
||||
/**
|
||||
* Retrieve a parsed and finalized Karma `Config` instance. This `karmaConfig`
|
||||
* object may be used to configure public API methods such a `Server`,
|
||||
* `runner.run`, and `stopper.stop`.
|
||||
*
|
||||
* @param {?string} [configFilePath=null]
|
||||
* A string representing a file system path pointing to the config file
|
||||
* whose default export is a function that will be used to set Karma
|
||||
* configuration options. This function will be passed an instance of the
|
||||
* `Config` class as its first argument. If this option is not provided,
|
||||
* then only the options provided by the `cliOptions` argument will be
|
||||
* set.
|
||||
* @param {Object} cliOptions
|
||||
* An object whose values will take priority over options set in the
|
||||
* config file. The config object passed to function exported by the
|
||||
* config file will already have these options applied. Any changes the
|
||||
* config file makes to these options will effectively be ignored in the
|
||||
* final configuration.
|
||||
*
|
||||
* `cliOptions` all the same options as the config file and is applied
|
||||
* using the same `config.set()` method.
|
||||
* @param {Object} parseOptions
|
||||
* @param {boolean} [parseOptions.promiseConfig=false]
|
||||
* When `true`, a promise that resolves to a `Config` object will be
|
||||
* returned. This also allows the function exported by config files (if
|
||||
* provided) to be asynchronous by returning a promise. Resolving this
|
||||
* promise indicates that all async activity has completed. The resolution
|
||||
* value itself is ignored, all configuration must be done with
|
||||
* `config.set`.
|
||||
* @param {boolean} [parseOptions.throwErrors=false]
|
||||
* When `true`, process exiting on critical failures will be disabled. In
|
||||
* The error will be thrown as an exception. If
|
||||
* `parseOptions.promiseConfig` is also `true`, then the error will
|
||||
* instead be used as the promise's reject reason.
|
||||
* @returns {Config|Promise<Config>}
|
||||
*/
|
||||
function parseConfig (configFilePath, cliOptions, parseOptions) {
|
||||
const promiseConfig = parseOptions && parseOptions.promiseConfig === true
|
||||
const throwErrors = parseOptions && parseOptions.throwErrors === true
|
||||
const shouldSetupLoggerEarly = promiseConfig
|
||||
if (shouldSetupLoggerEarly) {
|
||||
// `setupFromConfig` provides defaults for `colors` and `logLevel`.
|
||||
// `setup` provides defaults for `appenders`
|
||||
// The first argument MUST BE an object
|
||||
logger.setupFromConfig({})
|
||||
}
|
||||
function fail () {
|
||||
log.error(...arguments)
|
||||
if (throwErrors) {
|
||||
const errorMessage = Array.from(arguments).join(' ')
|
||||
const err = new Error(errorMessage)
|
||||
if (promiseConfig) {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
throw err
|
||||
} else {
|
||||
const warningMessage =
|
||||
'The `parseConfig()` function historically called `process.exit(1)`' +
|
||||
' when it failed. This behavior is now deprecated and function will' +
|
||||
' throw an error in the next major release. To suppress this warning' +
|
||||
' pass `throwErrors: true` as a third argument to opt-in into the new' +
|
||||
' behavior and adjust your code to respond to the exception' +
|
||||
' accordingly.' +
|
||||
' Example: `parseConfig(path, cliOptions, { throwErrors: true })`'
|
||||
log.warn(warningMessage)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
let configModule
|
||||
if (configFilePath) {
|
||||
try {
|
||||
if (path.extname(configFilePath) === '.ts' && TYPE_SCRIPT_AVAILABLE) {
|
||||
require('ts-node').register()
|
||||
}
|
||||
configModule = require(configFilePath)
|
||||
if (typeof configModule === 'object' && typeof configModule.default !== 'undefined') {
|
||||
configModule = configModule.default
|
||||
}
|
||||
} catch (e) {
|
||||
const extension = path.extname(configFilePath)
|
||||
if (extension === '.coffee' && !COFFEE_SCRIPT_AVAILABLE) {
|
||||
log.error('You need to install CoffeeScript.\n npm install coffeescript --save-dev')
|
||||
} else if (extension === '.ls' && !LIVE_SCRIPT_AVAILABLE) {
|
||||
log.error('You need to install LiveScript.\n npm install LiveScript --save-dev')
|
||||
} else if (extension === '.ts' && !TYPE_SCRIPT_AVAILABLE) {
|
||||
log.error('You need to install TypeScript.\n npm install typescript ts-node --save-dev')
|
||||
}
|
||||
return fail('Error in config file!\n ' + e.stack || e)
|
||||
}
|
||||
if (!helper.isFunction(configModule)) {
|
||||
return fail('Config file must export a function!\n' + CONFIG_SYNTAX_HELP)
|
||||
}
|
||||
} else {
|
||||
configModule = () => {} // if no config file path is passed, we define a dummy config module.
|
||||
}
|
||||
|
||||
const config = new Config()
|
||||
|
||||
// save and reset hostname and listenAddress so we can detect if the user
|
||||
// changed them
|
||||
const defaultHostname = config.hostname
|
||||
config.hostname = null
|
||||
const defaultListenAddress = config.listenAddress
|
||||
config.listenAddress = null
|
||||
|
||||
// add the user's configuration in
|
||||
config.set(cliOptions)
|
||||
|
||||
let configModuleReturn
|
||||
try {
|
||||
configModuleReturn = configModule(config)
|
||||
} catch (e) {
|
||||
return fail('Error in config file!\n', e)
|
||||
}
|
||||
function finalizeConfig (config) {
|
||||
// merge the config from config file and cliOptions (precedence)
|
||||
config.set(cliOptions)
|
||||
|
||||
// if the user changed listenAddress, but didn't set a hostname, warn them
|
||||
if (config.hostname === null && config.listenAddress !== null) {
|
||||
log.warn(`ListenAddress was set to ${config.listenAddress} but hostname was left as the default: ` +
|
||||
`${defaultHostname}. If your browsers fail to connect, consider changing the hostname option.`)
|
||||
}
|
||||
// restore values that weren't overwritten by the user
|
||||
if (config.hostname === null) {
|
||||
config.hostname = defaultHostname
|
||||
}
|
||||
if (config.listenAddress === null) {
|
||||
config.listenAddress = defaultListenAddress
|
||||
}
|
||||
|
||||
// configure the logger as soon as we can
|
||||
logger.setup(config.logLevel, config.colors, config.loggers)
|
||||
|
||||
log.debug(configFilePath ? `Loading config ${configFilePath}` : 'No config file specified.')
|
||||
|
||||
return normalizeConfig(config, configFilePath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return value is a function or (non-null) object that has a `then` method.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @see {@link https://promisesaplus.com/}
|
||||
*/
|
||||
const returnIsThenable = (
|
||||
(
|
||||
(configModuleReturn != null && typeof configModuleReturn === 'object') ||
|
||||
typeof configModuleReturn === 'function'
|
||||
) && typeof configModuleReturn.then === 'function'
|
||||
)
|
||||
if (returnIsThenable) {
|
||||
if (promiseConfig !== true) {
|
||||
const errorMessage =
|
||||
'The `parseOptions.promiseConfig` option must be set to `true` to ' +
|
||||
'enable promise return values from configuration files. ' +
|
||||
'Example: `parseConfig(path, cliOptions, { promiseConfig: true })`'
|
||||
return fail(errorMessage)
|
||||
}
|
||||
return configModuleReturn.then(
|
||||
function onKarmaConfigModuleFulfilled (/* ignoredResolutionValue */) {
|
||||
return finalizeConfig(config)
|
||||
},
|
||||
function onKarmaConfigModuleRejected (reason) {
|
||||
return fail('Error in config file!\n', reason)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
if (promiseConfig) {
|
||||
try {
|
||||
return Promise.resolve(finalizeConfig(config))
|
||||
} catch (exception) {
|
||||
return Promise.reject(exception)
|
||||
}
|
||||
} else {
|
||||
return finalizeConfig(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
exports.parseConfig = parseConfig
|
||||
exports.Pattern = Pattern
|
||||
exports.createPatternObject = createPatternObject
|
||||
exports.Config = Config
|
43
my-app/node_modules/karma/lib/constants.js
generated
vendored
Executable file
43
my-app/node_modules/karma/lib/constants.js
generated
vendored
Executable file
|
@ -0,0 +1,43 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const path = require('path')
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '/../package.json')).toString())
|
||||
|
||||
exports.VERSION = pkg.version
|
||||
|
||||
exports.DEFAULT_PORT = process.env.PORT || 9876
|
||||
exports.DEFAULT_HOSTNAME = process.env.IP || 'localhost'
|
||||
exports.DEFAULT_LISTEN_ADDR = process.env.LISTEN_ADDR || '0.0.0.0'
|
||||
|
||||
// log levels
|
||||
exports.LOG_DISABLE = 'OFF'
|
||||
exports.LOG_ERROR = 'ERROR'
|
||||
exports.LOG_WARN = 'WARN'
|
||||
exports.LOG_INFO = 'INFO'
|
||||
exports.LOG_DEBUG = 'DEBUG'
|
||||
exports.LOG_LOG = 'LOG'
|
||||
exports.LOG_PRIORITIES = [
|
||||
exports.LOG_DISABLE,
|
||||
exports.LOG_ERROR,
|
||||
exports.LOG_WARN,
|
||||
exports.LOG_LOG,
|
||||
exports.LOG_INFO,
|
||||
exports.LOG_DEBUG
|
||||
]
|
||||
|
||||
// Default patterns for the pattern layout.
|
||||
exports.COLOR_PATTERN = '%[%d{DATETIME}:%p [%c]: %]%m'
|
||||
exports.NO_COLOR_PATTERN = '%d{DATETIME}:%p [%c]: %m'
|
||||
|
||||
// Default console appender
|
||||
exports.CONSOLE_APPENDER = {
|
||||
type: 'console',
|
||||
layout: {
|
||||
type: 'pattern',
|
||||
pattern: exports.COLOR_PATTERN
|
||||
}
|
||||
}
|
||||
|
||||
exports.EXIT_CODE = '\x1FEXIT'
|
11
my-app/node_modules/karma/lib/detached.js
generated
vendored
Executable file
11
my-app/node_modules/karma/lib/detached.js
generated
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
|
||||
const Server = require('./server')
|
||||
const configurationFile = process.argv[2]
|
||||
const fileContents = fs.readFileSync(configurationFile, 'utf-8')
|
||||
fs.unlink(configurationFile, function () {})
|
||||
const data = JSON.parse(fileContents)
|
||||
const server = new Server(data)
|
||||
server.start(data)
|
33
my-app/node_modules/karma/lib/emitter_wrapper.js
generated
vendored
Executable file
33
my-app/node_modules/karma/lib/emitter_wrapper.js
generated
vendored
Executable file
|
@ -0,0 +1,33 @@
|
|||
'use strict'
|
||||
|
||||
class EmitterWrapper {
|
||||
constructor (emitter) {
|
||||
this.listeners = {}
|
||||
this.emitter = emitter
|
||||
}
|
||||
|
||||
addListener (event, listener) {
|
||||
this.emitter.addListener(event, listener)
|
||||
this.listeners[event] = this.listeners[event] || []
|
||||
this.listeners[event].push(listener)
|
||||
return this
|
||||
}
|
||||
|
||||
on (event, listener) {
|
||||
return this.addListener(event, listener)
|
||||
}
|
||||
|
||||
removeAllListeners (event) {
|
||||
const events = event ? [event] : Object.keys(this.listeners)
|
||||
events.forEach((event) => {
|
||||
this.listeners[event].forEach((listener) => {
|
||||
this.emitter.removeListener(event, listener)
|
||||
})
|
||||
delete this.listeners[event]
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EmitterWrapper
|
67
my-app/node_modules/karma/lib/events.js
generated
vendored
Executable file
67
my-app/node_modules/karma/lib/events.js
generated
vendored
Executable file
|
@ -0,0 +1,67 @@
|
|||
'use strict'
|
||||
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const helper = require('./helper')
|
||||
|
||||
function bufferEvents (emitter, eventsToBuffer) {
|
||||
const listeners = []
|
||||
const eventsToReply = []
|
||||
|
||||
function genericListener () {
|
||||
eventsToReply.push(Array.from(arguments))
|
||||
}
|
||||
|
||||
eventsToBuffer.forEach((eventName) => {
|
||||
const listener = genericListener.bind(null, eventName)
|
||||
listeners.push(listener)
|
||||
emitter.on(eventName, listener)
|
||||
})
|
||||
|
||||
return function () {
|
||||
listeners.forEach((listener, i) => {
|
||||
emitter.removeListener(eventsToBuffer[i], listener)
|
||||
})
|
||||
|
||||
eventsToReply.forEach((args) => {
|
||||
EventEmitter.prototype.emit.apply(emitter, args)
|
||||
})
|
||||
|
||||
listeners.length = 0
|
||||
eventsToReply.length = 0
|
||||
}
|
||||
}
|
||||
|
||||
class KarmaEventEmitter extends EventEmitter {
|
||||
bind (object) {
|
||||
for (const method in object) {
|
||||
if (method.startsWith('on') && helper.isFunction(object[method])) {
|
||||
this.on(helper.camelToSnake(method.slice(2)), function () {
|
||||
// We do not use an arrow function here, to supply the caller as this.
|
||||
object[method].apply(object, Array.from(arguments).concat(this))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitAsync (name) {
|
||||
// TODO(vojta): allow passing args
|
||||
// TODO(vojta): ignore/throw if listener call done() multiple times
|
||||
let pending = this.listeners(name).length
|
||||
const deferred = helper.defer()
|
||||
|
||||
this.emit(name, () => {
|
||||
if (!--pending) {
|
||||
deferred.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
if (!pending) {
|
||||
deferred.resolve()
|
||||
}
|
||||
|
||||
return deferred.promise
|
||||
}
|
||||
}
|
||||
|
||||
exports.EventEmitter = KarmaEventEmitter
|
||||
exports.bufferEvents = bufferEvents
|
96
my-app/node_modules/karma/lib/executor.js
generated
vendored
Executable file
96
my-app/node_modules/karma/lib/executor.js
generated
vendored
Executable file
|
@ -0,0 +1,96 @@
|
|||
'use strict'
|
||||
|
||||
const log = require('./logger').create()
|
||||
|
||||
class Executor {
|
||||
constructor (capturedBrowsers, config, emitter) {
|
||||
this.capturedBrowsers = capturedBrowsers
|
||||
this.config = config
|
||||
this.emitter = emitter
|
||||
|
||||
this.executionScheduled = false
|
||||
this.errorsScheduled = []
|
||||
this.pendingCount = 0
|
||||
this.runningBrowsers = null
|
||||
|
||||
this.emitter.on('run_complete', () => this.onRunComplete())
|
||||
this.emitter.on('browser_complete', () => this.onBrowserComplete())
|
||||
}
|
||||
|
||||
schedule () {
|
||||
if (this.capturedBrowsers.length === 0) {
|
||||
log.warn(`No captured browser, open ${this.config.protocol}//${this.config.hostname}:${this.config.port}${this.config.urlRoot}`)
|
||||
return false
|
||||
} else if (this.capturedBrowsers.areAllReady()) {
|
||||
log.debug('All browsers are ready, executing')
|
||||
log.debug(`Captured ${this.capturedBrowsers.length} browsers`)
|
||||
this.executionScheduled = false
|
||||
this.capturedBrowsers.clearResults()
|
||||
this.pendingCount = this.capturedBrowsers.length
|
||||
this.runningBrowsers = this.capturedBrowsers.clone()
|
||||
this.emitter.emit('run_start', this.runningBrowsers)
|
||||
this.socketIoSockets.emit('execute', this.config.client)
|
||||
return true
|
||||
} else {
|
||||
log.info('Delaying execution, these browsers are not ready: ' + this.capturedBrowsers.getNonReady().join(', '))
|
||||
this.executionScheduled = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule an error to be reported
|
||||
* @param {string} errorMessage
|
||||
* @returns {boolean} a boolean indicating whether or not the error was handled synchronously
|
||||
*/
|
||||
scheduleError (errorMessage) {
|
||||
// We don't want to interfere with any running test.
|
||||
// Verify that no test is running before reporting the error.
|
||||
if (this.capturedBrowsers.areAllReady()) {
|
||||
log.warn(errorMessage)
|
||||
const errorResult = {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
skipped: 0,
|
||||
error: errorMessage,
|
||||
exitCode: 1
|
||||
}
|
||||
const noBrowsersStartedTests = []
|
||||
this.emitter.emit('run_start', noBrowsersStartedTests) // A run cannot complete without being started
|
||||
this.emitter.emit('run_complete', noBrowsersStartedTests, errorResult)
|
||||
return true
|
||||
} else {
|
||||
this.errorsScheduled.push(errorMessage)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
onRunComplete () {
|
||||
if (this.executionScheduled) {
|
||||
this.schedule()
|
||||
}
|
||||
if (this.errorsScheduled.length) {
|
||||
const errorsToReport = this.errorsScheduled
|
||||
this.errorsScheduled = []
|
||||
errorsToReport.forEach((error) => this.scheduleError(error))
|
||||
}
|
||||
}
|
||||
|
||||
onBrowserComplete () {
|
||||
this.pendingCount--
|
||||
|
||||
if (!this.pendingCount) {
|
||||
// Ensure run_complete is emitted in the next tick
|
||||
// so it is never emitted before browser_complete
|
||||
setTimeout(() => {
|
||||
this.emitter.emit('run_complete', this.runningBrowsers, this.runningBrowsers.getResults())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executor.factory = function (capturedBrowsers, config, emitter) {
|
||||
return new Executor(capturedBrowsers, config, emitter)
|
||||
}
|
||||
|
||||
module.exports = Executor
|
242
my-app/node_modules/karma/lib/file-list.js
generated
vendored
Executable file
242
my-app/node_modules/karma/lib/file-list.js
generated
vendored
Executable file
|
@ -0,0 +1,242 @@
|
|||
'use strict'
|
||||
|
||||
const { promisify } = require('util')
|
||||
const mm = require('minimatch')
|
||||
const Glob = require('glob').Glob
|
||||
const fs = require('graceful-fs')
|
||||
const statAsync = promisify(fs.stat.bind(fs))
|
||||
const pathLib = require('path')
|
||||
const _ = require('lodash')
|
||||
|
||||
const File = require('./file')
|
||||
const Url = require('./url')
|
||||
const helper = require('./helper')
|
||||
const log = require('./logger').create('filelist')
|
||||
const createPatternObject = require('./config').createPatternObject
|
||||
|
||||
class FileList {
|
||||
constructor (patterns, excludes, emitter, preprocess, autoWatchBatchDelay) {
|
||||
this._patterns = patterns || []
|
||||
this._excludes = excludes || []
|
||||
this._emitter = emitter
|
||||
this._preprocess = preprocess
|
||||
|
||||
this.buckets = new Map()
|
||||
|
||||
// A promise that is pending if and only if we are active in this.refresh_()
|
||||
this._refreshing = null
|
||||
|
||||
const emit = () => {
|
||||
this._emitter.emit('file_list_modified', this.files)
|
||||
}
|
||||
|
||||
const debouncedEmit = _.debounce(emit, autoWatchBatchDelay)
|
||||
this._emitModified = (immediate) => {
|
||||
immediate ? emit() : debouncedEmit()
|
||||
}
|
||||
}
|
||||
|
||||
_findExcluded (path) {
|
||||
return this._excludes.find((pattern) => mm(path, pattern))
|
||||
}
|
||||
|
||||
_findIncluded (path) {
|
||||
return this._patterns.find((pattern) => mm(path, pattern.pattern))
|
||||
}
|
||||
|
||||
_findFile (path, pattern) {
|
||||
if (!path || !pattern) return
|
||||
return this._getFilesByPattern(pattern.pattern).find((file) => file.originalPath === path)
|
||||
}
|
||||
|
||||
_exists (path) {
|
||||
return !!this._patterns.find((pattern) => mm(path, pattern.pattern) && this._findFile(path, pattern))
|
||||
}
|
||||
|
||||
_getFilesByPattern (pattern) {
|
||||
return this.buckets.get(pattern) || []
|
||||
}
|
||||
|
||||
_refresh () {
|
||||
const matchedFiles = new Set()
|
||||
|
||||
let lastCompletedRefresh = this._refreshing
|
||||
lastCompletedRefresh = Promise.all(
|
||||
this._patterns.map(async ({ pattern, type, nocache, isBinary, integrity }) => {
|
||||
if (helper.isUrlAbsolute(pattern)) {
|
||||
this.buckets.set(pattern, [new Url(pattern, type, integrity)])
|
||||
return
|
||||
}
|
||||
|
||||
const mg = new Glob(pathLib.normalize(pattern), { cwd: '/', follow: true, nodir: true, sync: true })
|
||||
|
||||
const files = mg.found
|
||||
.filter((path) => {
|
||||
if (this._findExcluded(path)) {
|
||||
log.debug(`Excluded file "${path}"`)
|
||||
return false
|
||||
} else if (matchedFiles.has(path)) {
|
||||
return false
|
||||
} else {
|
||||
matchedFiles.add(path)
|
||||
return true
|
||||
}
|
||||
})
|
||||
.map((path) => new File(path, mg.statCache[path].mtime, nocache, type, isBinary))
|
||||
|
||||
if (nocache) {
|
||||
log.debug(`Not preprocessing "${pattern}" due to nocache`)
|
||||
} else {
|
||||
await Promise.all(files.map((file) => this._preprocess(file)))
|
||||
}
|
||||
|
||||
this.buckets.set(pattern, files)
|
||||
|
||||
if (_.isEmpty(mg.found)) {
|
||||
log.warn(`Pattern "${pattern}" does not match any file.`)
|
||||
} else if (_.isEmpty(files)) {
|
||||
log.warn(`All files matched by "${pattern}" were excluded or matched by prior matchers.`)
|
||||
}
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
// When we return from this function the file processing chain will be
|
||||
// complete. In the case of two fast refresh() calls, the second call
|
||||
// will overwrite this._refreshing, and we want the status to reflect
|
||||
// the second call and skip the modification event from the first call.
|
||||
if (this._refreshing !== lastCompletedRefresh) {
|
||||
return this._refreshing
|
||||
}
|
||||
this._emitModified(true)
|
||||
return this.files
|
||||
})
|
||||
|
||||
return lastCompletedRefresh
|
||||
}
|
||||
|
||||
get files () {
|
||||
const served = []
|
||||
const included = {}
|
||||
const lookup = {}
|
||||
this._patterns.forEach((p) => {
|
||||
// This needs to be here sadly, as plugins are modifiying
|
||||
// the _patterns directly resulting in elements not being
|
||||
// instantiated properly
|
||||
if (p.constructor.name !== 'Pattern') {
|
||||
p = createPatternObject(p)
|
||||
}
|
||||
|
||||
const files = this._getFilesByPattern(p.pattern)
|
||||
files.sort((a, b) => {
|
||||
if (a.path > b.path) return 1
|
||||
if (a.path < b.path) return -1
|
||||
|
||||
return 0
|
||||
})
|
||||
|
||||
if (p.served) {
|
||||
served.push(...files)
|
||||
}
|
||||
|
||||
files.forEach((file) => {
|
||||
if (lookup[file.path] && lookup[file.path].compare(p) < 0) return
|
||||
|
||||
lookup[file.path] = p
|
||||
if (p.included) {
|
||||
included[file.path] = file
|
||||
} else {
|
||||
delete included[file.path]
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
served: _.uniq(served, 'path'),
|
||||
included: _.values(included)
|
||||
}
|
||||
}
|
||||
|
||||
refresh () {
|
||||
this._refreshing = this._refresh()
|
||||
return this._refreshing
|
||||
}
|
||||
|
||||
reload (patterns, excludes) {
|
||||
this._patterns = patterns || []
|
||||
this._excludes = excludes || []
|
||||
|
||||
return this.refresh()
|
||||
}
|
||||
|
||||
async addFile (path) {
|
||||
const excluded = this._findExcluded(path)
|
||||
if (excluded) {
|
||||
log.debug(`Add file "${path}" ignored. Excluded by "${excluded}".`)
|
||||
return this.files
|
||||
}
|
||||
|
||||
const pattern = this._findIncluded(path)
|
||||
if (!pattern) {
|
||||
log.debug(`Add file "${path}" ignored. Does not match any pattern.`)
|
||||
return this.files
|
||||
}
|
||||
|
||||
if (this._exists(path)) {
|
||||
log.debug(`Add file "${path}" ignored. Already in the list.`)
|
||||
return this.files
|
||||
}
|
||||
|
||||
const file = new File(path)
|
||||
this._getFilesByPattern(pattern.pattern).push(file)
|
||||
|
||||
const [stat] = await Promise.all([statAsync(path), this._refreshing])
|
||||
file.mtime = stat.mtime
|
||||
await this._preprocess(file)
|
||||
|
||||
log.info(`Added file "${path}".`)
|
||||
this._emitModified()
|
||||
return this.files
|
||||
}
|
||||
|
||||
async changeFile (path, force) {
|
||||
const pattern = this._findIncluded(path)
|
||||
const file = this._findFile(path, pattern)
|
||||
|
||||
if (!file) {
|
||||
log.debug(`Changed file "${path}" ignored. Does not match any file in the list.`)
|
||||
return this.files
|
||||
}
|
||||
|
||||
const [stat] = await Promise.all([statAsync(path), this._refreshing])
|
||||
if (force || stat.mtime > file.mtime) {
|
||||
file.mtime = stat.mtime
|
||||
await this._preprocess(file)
|
||||
log.info(`Changed file "${path}".`)
|
||||
this._emitModified(force)
|
||||
}
|
||||
return this.files
|
||||
}
|
||||
|
||||
async removeFile (path) {
|
||||
const pattern = this._findIncluded(path)
|
||||
const file = this._findFile(path, pattern)
|
||||
|
||||
if (file) {
|
||||
helper.arrayRemove(this._getFilesByPattern(pattern.pattern), file)
|
||||
log.info(`Removed file "${path}".`)
|
||||
|
||||
this._emitModified()
|
||||
} else {
|
||||
log.debug(`Removed file "${path}" ignored. Does not match any file in the list.`)
|
||||
}
|
||||
return this.files
|
||||
}
|
||||
}
|
||||
|
||||
FileList.factory = function (config, emitter, preprocess) {
|
||||
return new FileList(config.files, config.exclude, emitter, preprocess, config.autoWatchBatchDelay)
|
||||
}
|
||||
|
||||
FileList.factory.$inject = ['config', 'emitter', 'preprocess']
|
||||
|
||||
module.exports = FileList
|
49
my-app/node_modules/karma/lib/file.js
generated
vendored
Executable file
49
my-app/node_modules/karma/lib/file.js
generated
vendored
Executable file
|
@ -0,0 +1,49 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
/**
|
||||
* File object used for tracking files in `file-list.js`.
|
||||
*/
|
||||
class File {
|
||||
constructor (path, mtime, doNotCache, type, isBinary, integrity) {
|
||||
// used for serving (processed path, eg some/file.coffee -> some/file.coffee.js)
|
||||
this.path = path
|
||||
|
||||
// original absolute path, id of the file
|
||||
this.originalPath = path
|
||||
|
||||
// where the content is stored (processed)
|
||||
this.contentPath = path
|
||||
|
||||
// encodings format {[encodingType]: encodedContent}
|
||||
// example: {gzip: <Buffer 1f 8b 08...>}
|
||||
this.encodings = Object.create(null)
|
||||
|
||||
this.mtime = mtime
|
||||
this.isUrl = false
|
||||
|
||||
this.doNotCache = doNotCache === undefined ? false : doNotCache
|
||||
|
||||
this.type = type
|
||||
|
||||
// Tri state: null means probe file for binary.
|
||||
this.isBinary = isBinary === undefined ? null : isBinary
|
||||
|
||||
this.integrity = integrity
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect type from the file extension.
|
||||
* @returns {string} detected file type or empty string
|
||||
*/
|
||||
detectType () {
|
||||
return path.extname(this.path).slice(1)
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.path
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = File
|
171
my-app/node_modules/karma/lib/helper.js
generated
vendored
Executable file
171
my-app/node_modules/karma/lib/helper.js
generated
vendored
Executable file
|
@ -0,0 +1,171 @@
|
|||
'use strict'
|
||||
|
||||
const _ = require('lodash')
|
||||
const mkdirp = require('mkdirp')
|
||||
const useragent = require('ua-parser-js')
|
||||
const mm = require('minimatch')
|
||||
|
||||
exports.browserFullNameToShort = (fullName) => {
|
||||
const ua = useragent(fullName)
|
||||
if (!ua.browser.name && !ua.browser.version && !ua.os.name && !ua.os.version) {
|
||||
return fullName
|
||||
}
|
||||
return `${ua.browser.name} ${ua.browser.version || '0.0.0'} (${ua.os.name} ${ua.os.version || '0.0.0'})`
|
||||
}
|
||||
|
||||
exports.isDefined = (value) => {
|
||||
return !_.isUndefined(value)
|
||||
}
|
||||
|
||||
const parser = (pattern, out) => {
|
||||
if (pattern.length === 0) return out
|
||||
const p = /^(\[[^\]]*\]|[*+@?]\((.+?)\))/g
|
||||
const matches = p.exec(pattern)
|
||||
if (!matches) {
|
||||
const c = pattern[0]
|
||||
let t = 'word'
|
||||
if (c === '*') {
|
||||
t = 'star'
|
||||
} else if (c === '?') {
|
||||
t = 'optional'
|
||||
}
|
||||
out[t]++
|
||||
return parser(pattern.slice(1), out)
|
||||
}
|
||||
if (matches[2] !== undefined) {
|
||||
out.ext_glob++
|
||||
parser(matches[2], out)
|
||||
return parser(pattern.slice(matches[0].length), out)
|
||||
}
|
||||
out.range++
|
||||
return parser(pattern.slice(matches[0].length), out)
|
||||
}
|
||||
|
||||
const gsParser = (pattern, out) => {
|
||||
if (pattern === '**') {
|
||||
out.glob_star++
|
||||
return out
|
||||
}
|
||||
return parser(pattern, out)
|
||||
}
|
||||
|
||||
const compareWeightObject = (w1, w2) => {
|
||||
return exports.mmComparePatternWeights(
|
||||
[w1.glob_star, w1.star, w1.ext_glob, w1.range, w1.optional],
|
||||
[w2.glob_star, w2.star, w2.ext_glob, w2.range, w2.optional]
|
||||
)
|
||||
}
|
||||
|
||||
exports.mmPatternWeight = (pattern) => {
|
||||
const m = new mm.Minimatch(pattern)
|
||||
if (!m.globParts) return [0, 0, 0, 0, 0, 0]
|
||||
const result = m.globParts.reduce((prev, p) => {
|
||||
const r = p.reduce((prev, p) => {
|
||||
return gsParser(p, prev)
|
||||
}, { glob_star: 0, ext_glob: 0, word: 0, star: 0, optional: 0, range: 0 })
|
||||
if (prev === undefined) return r
|
||||
return compareWeightObject(r, prev) > 0 ? r : prev
|
||||
}, undefined)
|
||||
result.glob_sets = m.set.length
|
||||
return [result.glob_sets, result.glob_star, result.star, result.ext_glob, result.range, result.optional]
|
||||
}
|
||||
|
||||
exports.mmComparePatternWeights = (weight1, weight2) => {
|
||||
const n1 = weight1[0]
|
||||
const n2 = weight2[0]
|
||||
const diff = n1 - n2
|
||||
if (diff !== 0) return diff / Math.abs(diff)
|
||||
return weight1.length > 1 ? exports.mmComparePatternWeights(weight1.slice(1), weight2.slice(1)) : 0
|
||||
}
|
||||
|
||||
exports.isFunction = _.isFunction
|
||||
exports.isString = _.isString
|
||||
exports.isObject = _.isObject
|
||||
exports.isArray = _.isArray
|
||||
exports.isNumber = _.isNumber
|
||||
|
||||
const ABS_URL = /^https?:\/\//
|
||||
exports.isUrlAbsolute = (url) => {
|
||||
return ABS_URL.test(url)
|
||||
}
|
||||
|
||||
exports.camelToSnake = (camelCase) => {
|
||||
return camelCase.replace(/[A-Z]/g, (match, pos) => {
|
||||
return (pos > 0 ? '_' : '') + match.toLowerCase()
|
||||
})
|
||||
}
|
||||
|
||||
exports.ucFirst = (word) => {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1)
|
||||
}
|
||||
|
||||
exports.dashToCamel = (dash) => {
|
||||
const words = dash.split('-')
|
||||
return words.shift() + words.map(exports.ucFirst).join('')
|
||||
}
|
||||
|
||||
exports.arrayRemove = (collection, item) => {
|
||||
const idx = collection.indexOf(item)
|
||||
|
||||
if (idx !== -1) {
|
||||
collection.splice(idx, 1)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
exports.merge = function () {
|
||||
const args = Array.prototype.slice.call(arguments, 0)
|
||||
args.unshift({})
|
||||
return _.merge.apply({}, args)
|
||||
}
|
||||
|
||||
exports.formatTimeInterval = (time) => {
|
||||
const mins = Math.floor(time / 60000)
|
||||
const secs = (time - mins * 60000) / 1000
|
||||
let str = secs + (secs === 1 ? ' sec' : ' secs')
|
||||
|
||||
if (mins) {
|
||||
str = mins + (mins === 1 ? ' min ' : ' mins ') + str
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
const replaceWinPath = (path) => {
|
||||
return _.isString(path) ? path.replace(/\\/g, '/') : path
|
||||
}
|
||||
|
||||
exports.normalizeWinPath = process.platform === 'win32' ? replaceWinPath : _.identity
|
||||
|
||||
exports.mkdirIfNotExists = (directory, done) => {
|
||||
mkdirp(directory, done)
|
||||
}
|
||||
|
||||
exports.defer = () => {
|
||||
let res
|
||||
let rej
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
res = resolve
|
||||
rej = reject
|
||||
})
|
||||
|
||||
return {
|
||||
resolve: res,
|
||||
reject: rej,
|
||||
promise: promise
|
||||
}
|
||||
}
|
||||
|
||||
exports.saveOriginalArgs = (config) => {
|
||||
if (config && config.client && config.client.args) {
|
||||
config.client.originalArgs = _.cloneDeep(config.client.args)
|
||||
}
|
||||
}
|
||||
|
||||
exports.restoreOriginalArgs = (config) => {
|
||||
if (config && config.client && config.client.originalArgs) {
|
||||
config.client.args = _.cloneDeep(config.client.originalArgs)
|
||||
}
|
||||
}
|
18
my-app/node_modules/karma/lib/index.js
generated
vendored
Executable file
18
my-app/node_modules/karma/lib/index.js
generated
vendored
Executable file
|
@ -0,0 +1,18 @@
|
|||
'use strict'
|
||||
|
||||
const constants = require('./constants')
|
||||
const Server = require('./server')
|
||||
const runner = require('./runner')
|
||||
const stopper = require('./stopper')
|
||||
const launcher = require('./launcher')
|
||||
const cfg = require('./config')
|
||||
|
||||
module.exports = {
|
||||
constants: constants,
|
||||
VERSION: constants.VERSION,
|
||||
Server: Server,
|
||||
runner: runner,
|
||||
stopper: stopper,
|
||||
launcher: launcher,
|
||||
config: { parseConfig: cfg.parseConfig } // lets start with only opening up the `parseConfig` api
|
||||
}
|
239
my-app/node_modules/karma/lib/init.js
generated
vendored
Executable file
239
my-app/node_modules/karma/lib/init.js
generated
vendored
Executable file
|
@ -0,0 +1,239 @@
|
|||
'use strict'
|
||||
|
||||
const readline = require('readline')
|
||||
const path = require('path')
|
||||
const glob = require('glob')
|
||||
const mm = require('minimatch')
|
||||
const exec = require('child_process').exec
|
||||
|
||||
const helper = require('./helper')
|
||||
const logger = require('./logger')
|
||||
|
||||
const log = logger.create('init')
|
||||
const logQueue = require('./init/log-queue')
|
||||
|
||||
const StateMachine = require('./init/state_machine')
|
||||
const COLOR_SCHEME = require('./init/color_schemes')
|
||||
const formatters = require('./init/formatters')
|
||||
|
||||
// TODO(vojta): coverage
|
||||
// TODO(vojta): html preprocessors
|
||||
// TODO(vojta): SauceLabs
|
||||
// TODO(vojta): BrowserStack
|
||||
|
||||
let NODE_MODULES_DIR = path.resolve(__dirname, '../..')
|
||||
|
||||
// Karma is not in node_modules, probably a symlink,
|
||||
// use current working dir.
|
||||
if (!/node_modules$/.test(NODE_MODULES_DIR)) {
|
||||
NODE_MODULES_DIR = path.resolve('node_modules')
|
||||
}
|
||||
|
||||
function installPackage (pkgName) {
|
||||
// Do not install if already installed.
|
||||
try {
|
||||
require(NODE_MODULES_DIR + '/' + pkgName)
|
||||
return
|
||||
} catch (e) {}
|
||||
|
||||
log.debug(`Missing plugin "${pkgName}". Installing...`)
|
||||
|
||||
const options = {
|
||||
cwd: path.resolve(NODE_MODULES_DIR, '..')
|
||||
}
|
||||
|
||||
exec(`npm install ${pkgName} --save-dev`, options, function (err, stdout, stderr) {
|
||||
// Put the logs into the queue and print them after answering current question.
|
||||
// Otherwise the log would clobber the interactive terminal.
|
||||
logQueue.push(function () {
|
||||
if (!err) {
|
||||
log.debug(`${pkgName} successfully installed.`)
|
||||
} else if (/is not in the npm registry/.test(stderr)) {
|
||||
log.warn(`Failed to install "${pkgName}". It is not in the npm registry!\n Please install it manually.`)
|
||||
} else if (/Error: EACCES/.test(stderr)) {
|
||||
log.warn(`Failed to install "${pkgName}". No permissions to write in ${options.cwd}!\n Please install it manually.`)
|
||||
} else {
|
||||
log.warn(`Failed to install "${pkgName}"\n Please install it manually.`)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function validatePattern (pattern) {
|
||||
if (!glob.sync(pattern).length) {
|
||||
log.warn('There is no file matching this pattern.\n')
|
||||
}
|
||||
}
|
||||
|
||||
function validateBrowser (name) {
|
||||
// TODO(vojta): check if the path resolves to a binary
|
||||
installPackage('karma-' + name.toLowerCase().replace('headless', '').replace('canary', '') + '-launcher')
|
||||
}
|
||||
|
||||
function validateFramework (name) {
|
||||
installPackage('karma-' + name)
|
||||
}
|
||||
|
||||
function validateRequireJs (useRequire) {
|
||||
if (useRequire) {
|
||||
validateFramework('requirejs')
|
||||
}
|
||||
}
|
||||
|
||||
var questions = [{
|
||||
id: 'framework',
|
||||
question: 'Which testing framework do you want to use ?',
|
||||
hint: 'Press tab to list possible options. Enter to move to the next question.',
|
||||
options: ['jasmine', 'mocha', 'qunit', 'nodeunit', 'nunit', ''],
|
||||
validate: validateFramework
|
||||
}, {
|
||||
id: 'requirejs',
|
||||
question: 'Do you want to use Require.js ?',
|
||||
hint: 'This will add Require.js plugin.\nPress tab to list possible options. Enter to move to the next question.',
|
||||
options: ['no', 'yes'],
|
||||
validate: validateRequireJs,
|
||||
boolean: true
|
||||
}, {
|
||||
id: 'browsers',
|
||||
question: 'Do you want to capture any browsers automatically ?',
|
||||
hint: 'Press tab to list possible options. Enter empty string to move to the next question.',
|
||||
options: ['Chrome', 'ChromeHeadless', 'ChromeCanary', 'Firefox', 'Safari', 'PhantomJS', 'Opera', 'IE', ''],
|
||||
validate: validateBrowser,
|
||||
multiple: true
|
||||
}, {
|
||||
id: 'files',
|
||||
question: 'What is the location of your source and test files ?',
|
||||
hint: 'You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".\nEnter empty string to move to the next question.',
|
||||
multiple: true,
|
||||
validate: validatePattern
|
||||
}, {
|
||||
id: 'exclude',
|
||||
question: 'Should any of the files included by the previous patterns be excluded ?',
|
||||
hint: 'You can use glob patterns, eg. "**/*.swp".\nEnter empty string to move to the next question.',
|
||||
multiple: true,
|
||||
validate: validatePattern
|
||||
}, {
|
||||
id: 'generateTestMain',
|
||||
question: 'Do you wanna generate a bootstrap file for RequireJS?',
|
||||
hint: 'This will generate test-main.js/coffee that configures RequireJS and starts the tests.',
|
||||
options: ['no', 'yes'],
|
||||
boolean: true,
|
||||
condition: (answers) => answers.requirejs
|
||||
}, {
|
||||
id: 'includedFiles',
|
||||
question: 'Which files do you want to include with <script> tag ?',
|
||||
hint: 'This should be a script that bootstraps your test by configuring Require.js and ' +
|
||||
'kicking __karma__.start(), probably your test-main.js file.\n' +
|
||||
'Enter empty string to move to the next question.',
|
||||
multiple: true,
|
||||
validate: validatePattern,
|
||||
condition: (answers) => answers.requirejs && !answers.generateTestMain
|
||||
}, {
|
||||
id: 'autoWatch',
|
||||
question: 'Do you want Karma to watch all the files and run the tests on change ?',
|
||||
hint: 'Press tab to list possible options.',
|
||||
options: ['yes', 'no'],
|
||||
boolean: true
|
||||
}]
|
||||
|
||||
function getBasePath (configFilePath, cwd) {
|
||||
const configParts = path.dirname(configFilePath).split(path.sep)
|
||||
const cwdParts = cwd.split(path.sep)
|
||||
const base = []
|
||||
|
||||
while (configParts.length && configParts[0] === cwdParts[0]) {
|
||||
configParts.shift()
|
||||
cwdParts.shift()
|
||||
}
|
||||
|
||||
while (configParts.length) {
|
||||
const part = configParts.shift()
|
||||
if (part === '..') {
|
||||
base.unshift(cwdParts.pop())
|
||||
} else if (part !== '.') {
|
||||
base.unshift('..')
|
||||
}
|
||||
}
|
||||
|
||||
return base.join(path.sep)
|
||||
}
|
||||
|
||||
function processAnswers (answers, basePath, testMainFile) {
|
||||
const processedAnswers = {
|
||||
basePath: basePath,
|
||||
files: answers.files,
|
||||
onlyServedFiles: [],
|
||||
exclude: answers.exclude,
|
||||
autoWatch: answers.autoWatch,
|
||||
generateTestMain: answers.generateTestMain,
|
||||
browsers: answers.browsers,
|
||||
frameworks: [],
|
||||
preprocessors: {}
|
||||
}
|
||||
|
||||
if (answers.framework) {
|
||||
processedAnswers.frameworks.push(answers.framework)
|
||||
}
|
||||
|
||||
if (answers.requirejs) {
|
||||
processedAnswers.frameworks.push('requirejs')
|
||||
processedAnswers.files = answers.includedFiles || []
|
||||
processedAnswers.onlyServedFiles = answers.files
|
||||
|
||||
if (answers.generateTestMain) {
|
||||
processedAnswers.files.push(testMainFile)
|
||||
}
|
||||
}
|
||||
|
||||
const allPatterns = answers.files.concat(answers.includedFiles || [])
|
||||
if (allPatterns.some((pattern) => mm(pattern, '**/*.coffee'))) {
|
||||
installPackage('karma-coffee-preprocessor')
|
||||
processedAnswers.preprocessors['**/*.coffee'] = ['coffee']
|
||||
}
|
||||
|
||||
return processedAnswers
|
||||
}
|
||||
|
||||
exports.init = function (config) {
|
||||
logger.setupFromConfig(config)
|
||||
|
||||
const colorScheme = !helper.isDefined(config.colors) || config.colors ? COLOR_SCHEME.ON : COLOR_SCHEME.OFF
|
||||
// need to be registered before creating readlineInterface
|
||||
process.stdin.on('keypress', function (s, key) {
|
||||
sm.onKeypress(key)
|
||||
})
|
||||
|
||||
const rli = readline.createInterface(process.stdin, process.stdout)
|
||||
const sm = new StateMachine(rli, colorScheme)
|
||||
|
||||
rli.on('line', sm.onLine.bind(sm))
|
||||
|
||||
// clean colors
|
||||
rli.on('SIGINT', function () {
|
||||
sm.kill()
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
sm.process(questions, function (answers) {
|
||||
const cwd = process.cwd()
|
||||
const configFile = config.configFile || 'karma.conf.js'
|
||||
const isCoffee = path.extname(configFile) === '.coffee'
|
||||
const testMainFile = isCoffee ? 'test-main.coffee' : 'test-main.js'
|
||||
const formatter = formatters.createForPath(configFile)
|
||||
const processedAnswers = processAnswers(answers, getBasePath(configFile, cwd), testMainFile)
|
||||
const configFilePath = path.resolve(cwd, configFile)
|
||||
const testMainFilePath = path.resolve(cwd, testMainFile)
|
||||
|
||||
if (isCoffee) {
|
||||
installPackage('coffeescript')
|
||||
}
|
||||
|
||||
if (processedAnswers.generateTestMain) {
|
||||
formatter.writeRequirejsConfigFile(testMainFilePath)
|
||||
console.log(colorScheme.success(`RequireJS bootstrap file generated at "${testMainFilePath}".`))
|
||||
}
|
||||
|
||||
formatter.writeConfigFile(configFilePath, processedAnswers)
|
||||
console.log(colorScheme.success(`Config file generated at "${configFilePath}".`))
|
||||
})
|
||||
}
|
28
my-app/node_modules/karma/lib/init/color_schemes.js
generated
vendored
Executable file
28
my-app/node_modules/karma/lib/init/color_schemes.js
generated
vendored
Executable file
|
@ -0,0 +1,28 @@
|
|||
const COLORS_ON = {
|
||||
RESET: '\x1B[39m',
|
||||
ANSWER: '\x1B[36m', // NYAN
|
||||
SUCCESS: '\x1B[32m', // GREEN
|
||||
QUESTION: '\x1B[1m', // BOLD
|
||||
question: function (str) {
|
||||
return this.QUESTION + str + '\x1B[22m'
|
||||
},
|
||||
success: function (str) {
|
||||
return this.SUCCESS + str + this.RESET
|
||||
}
|
||||
}
|
||||
|
||||
const COLORS_OFF = {
|
||||
RESET: '',
|
||||
ANSWER: '',
|
||||
SUCCESS: '',
|
||||
QUESTION: '',
|
||||
question: function (str) {
|
||||
return str
|
||||
},
|
||||
success: function (str) {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
exports.ON = COLORS_ON
|
||||
exports.OFF = COLORS_OFF
|
116
my-app/node_modules/karma/lib/init/formatters.js
generated
vendored
Executable file
116
my-app/node_modules/karma/lib/init/formatters.js
generated
vendored
Executable file
|
@ -0,0 +1,116 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const FileUtils = require('../utils/file-utils')
|
||||
|
||||
function quote (value) {
|
||||
return `'${value}'`
|
||||
}
|
||||
|
||||
function formatLine (items) {
|
||||
return items.map(quote).join(', ')
|
||||
}
|
||||
|
||||
function formatMultiLines (items) {
|
||||
return items
|
||||
.map((file) => '\n ' + file)
|
||||
.join(',')
|
||||
}
|
||||
|
||||
function formatFiles (includedFiles, onlyServedFiles) {
|
||||
const lines = []
|
||||
.concat(includedFiles.map(quote))
|
||||
.concat(onlyServedFiles.map((file) => `{ pattern: ${quote(file)}, included: false }`))
|
||||
|
||||
return formatMultiLines(lines)
|
||||
}
|
||||
|
||||
function formatPreprocessors (preprocessors) {
|
||||
const lines = Object.keys(preprocessors)
|
||||
.map((pattern) => `${quote(pattern)}: [${formatLine(preprocessors[pattern])}]`)
|
||||
|
||||
return formatMultiLines(lines)
|
||||
}
|
||||
|
||||
function getConfigPath (file) {
|
||||
return path.join(__dirname, `/../../${file}`)
|
||||
}
|
||||
|
||||
class JavaScriptFormatter {
|
||||
constructor () {
|
||||
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.js')
|
||||
this.REQUIREJS_TEMPLATE_FILE = getConfigPath('requirejs.config.tpl.js')
|
||||
}
|
||||
|
||||
generateConfigFile (answers) {
|
||||
const replacements = this.formatAnswers(answers)
|
||||
|
||||
return FileUtils
|
||||
.readFile(this.TEMPLATE_FILE_PATH)
|
||||
.replace(/%(.*)%/g, (a, key) => replacements[key])
|
||||
}
|
||||
|
||||
writeConfigFile (path, answers) {
|
||||
FileUtils.saveFile(path, this.generateConfigFile(answers))
|
||||
}
|
||||
|
||||
writeRequirejsConfigFile (path) {
|
||||
FileUtils.copyFile(this.REQUIREJS_TEMPLATE_FILE, path)
|
||||
}
|
||||
|
||||
formatAnswers (answers) {
|
||||
return {
|
||||
DATE: new Date(),
|
||||
BASE_PATH: answers.basePath,
|
||||
FRAMEWORKS: formatLine(answers.frameworks),
|
||||
FILES: formatFiles(answers.files, answers.onlyServedFiles),
|
||||
EXCLUDE: formatFiles(answers.exclude, []),
|
||||
AUTO_WATCH: answers.autoWatch ? 'true' : 'false',
|
||||
BROWSERS: formatLine(answers.browsers),
|
||||
PREPROCESSORS: formatPreprocessors(answers.preprocessors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CoffeeFormatter extends JavaScriptFormatter {
|
||||
constructor () {
|
||||
super()
|
||||
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.coffee')
|
||||
this.REQUIREJS_TEMPLATE_FILE = getConfigPath('requirejs.config.tpl.coffee')
|
||||
}
|
||||
}
|
||||
|
||||
class LiveFormatter extends JavaScriptFormatter {
|
||||
constructor () {
|
||||
super()
|
||||
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.ls')
|
||||
}
|
||||
}
|
||||
|
||||
class TypeFormatter extends JavaScriptFormatter {
|
||||
constructor () {
|
||||
super()
|
||||
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.ts')
|
||||
}
|
||||
}
|
||||
|
||||
exports.JavaScript = JavaScriptFormatter
|
||||
exports.Coffee = CoffeeFormatter
|
||||
exports.Live = LiveFormatter
|
||||
exports.Type = TypeFormatter
|
||||
|
||||
exports.createForPath = function (path) {
|
||||
if (/\.coffee$/.test(path)) {
|
||||
return new CoffeeFormatter()
|
||||
}
|
||||
|
||||
if (/\.ls$/.test(path)) {
|
||||
return new LiveFormatter()
|
||||
}
|
||||
|
||||
if (/\.ts$/.test(path)) {
|
||||
return new TypeFormatter()
|
||||
}
|
||||
|
||||
return new JavaScriptFormatter()
|
||||
}
|
15
my-app/node_modules/karma/lib/init/log-queue.js
generated
vendored
Executable file
15
my-app/node_modules/karma/lib/init/log-queue.js
generated
vendored
Executable file
|
@ -0,0 +1,15 @@
|
|||
'use strict'
|
||||
|
||||
const logQueue = []
|
||||
function printLogQueue () {
|
||||
logQueue.forEach((log) => log())
|
||||
logQueue.length = 0
|
||||
}
|
||||
|
||||
function push (log) {
|
||||
logQueue.push(log)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
printLogQueue, push
|
||||
}
|
143
my-app/node_modules/karma/lib/init/state_machine.js
generated
vendored
Executable file
143
my-app/node_modules/karma/lib/init/state_machine.js
generated
vendored
Executable file
|
@ -0,0 +1,143 @@
|
|||
'use strict'
|
||||
|
||||
const logQueue = require('./log-queue')
|
||||
|
||||
let questions
|
||||
let currentQuestion
|
||||
let answers
|
||||
let currentOptions
|
||||
let currentOptionsPointer
|
||||
let currentQuestionId
|
||||
let done
|
||||
|
||||
class StateMachine {
|
||||
constructor (rli, colors) {
|
||||
this.rli = rli
|
||||
this.colors = colors
|
||||
}
|
||||
|
||||
showPrompt () {
|
||||
this.rli.write(this.colors.ANSWER)
|
||||
this.rli.prompt()
|
||||
}
|
||||
|
||||
onKeypress (key) {
|
||||
if (!currentOptions || !key) {
|
||||
return
|
||||
}
|
||||
|
||||
if (key.name === 'tab' || key.name === 'right' || key.name === 'down') {
|
||||
this.suggestOption(currentOptionsPointer + 1)
|
||||
} else if (key.name === 'left' || key.name === 'up') {
|
||||
this.suggestOption(currentOptionsPointer - 1)
|
||||
}
|
||||
|
||||
if (!key.ctrl && !key.meta && key.name !== 'enter' && key.name !== 'return') {
|
||||
key.name = 'escape'
|
||||
}
|
||||
}
|
||||
|
||||
suggestOption (index) {
|
||||
if (!currentOptions) {
|
||||
return
|
||||
}
|
||||
|
||||
if (index === -1) {
|
||||
currentOptionsPointer = currentOptions.length - 1
|
||||
} else if (index === currentOptions.length) {
|
||||
currentOptionsPointer = 0
|
||||
} else {
|
||||
currentOptionsPointer = index
|
||||
}
|
||||
|
||||
this.rli._deleteLineLeft()
|
||||
this.rli._deleteLineRight()
|
||||
this.rli.write(currentOptions[currentOptionsPointer])
|
||||
}
|
||||
|
||||
kill () {
|
||||
currentOptions = null
|
||||
currentQuestionId = null
|
||||
this.rli.write('\n' + this.colors.RESET + '\n')
|
||||
this.rli.close()
|
||||
}
|
||||
|
||||
onLine (line) {
|
||||
if (currentQuestionId) {
|
||||
this.rli.write(this.colors.RESET)
|
||||
line = line.trim().replace(this.colors.ANSWER, '').replace(this.colors.RESET, '')
|
||||
|
||||
if (currentOptions) {
|
||||
currentOptionsPointer = currentOptions.indexOf(line)
|
||||
if (currentOptionsPointer === -1) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (line === '') {
|
||||
line = null
|
||||
}
|
||||
|
||||
if (currentQuestion.boolean) {
|
||||
line = (line === 'yes' || line === 'true' || line === 'on')
|
||||
}
|
||||
|
||||
if (line !== null && currentQuestion.validate) {
|
||||
currentQuestion.validate(line)
|
||||
}
|
||||
|
||||
if (currentQuestion.multiple) {
|
||||
answers[currentQuestionId] = answers[currentQuestionId] || []
|
||||
if (line !== null) {
|
||||
answers[currentQuestionId].push(line)
|
||||
this.showPrompt()
|
||||
|
||||
if (currentOptions) {
|
||||
currentOptions.splice(currentOptionsPointer, 1)
|
||||
currentOptionsPointer = -1
|
||||
}
|
||||
} else {
|
||||
this.nextQuestion()
|
||||
}
|
||||
} else {
|
||||
answers[currentQuestionId] = line
|
||||
this.nextQuestion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextQuestion () {
|
||||
currentQuestion = questions.shift()
|
||||
|
||||
while (currentQuestion && currentQuestion.condition && !currentQuestion.condition(answers)) {
|
||||
currentQuestion = questions.shift()
|
||||
}
|
||||
|
||||
logQueue.printLogQueue()
|
||||
|
||||
if (currentQuestion) {
|
||||
currentQuestionId = null
|
||||
|
||||
this.rli.write('\n' + this.colors.question(currentQuestion.question) + '\n')
|
||||
this.rli.write(currentQuestion.hint + '\n')
|
||||
this.showPrompt()
|
||||
|
||||
currentOptions = currentQuestion.options || null
|
||||
currentQuestionId = currentQuestion.id
|
||||
this.suggestOption(0)
|
||||
} else {
|
||||
this.kill()
|
||||
done(answers)
|
||||
}
|
||||
}
|
||||
|
||||
process (_questions, _done) {
|
||||
questions = _questions
|
||||
answers = {}
|
||||
done = _done
|
||||
|
||||
this.nextQuestion()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StateMachine
|
199
my-app/node_modules/karma/lib/launcher.js
generated
vendored
Executable file
199
my-app/node_modules/karma/lib/launcher.js
generated
vendored
Executable file
|
@ -0,0 +1,199 @@
|
|||
'use strict'
|
||||
|
||||
const Jobs = require('qjobs')
|
||||
|
||||
const log = require('./logger').create('launcher')
|
||||
|
||||
const baseDecorator = require('./launchers/base').decoratorFactory
|
||||
const captureTimeoutDecorator = require('./launchers/capture_timeout').decoratorFactory
|
||||
const retryDecorator = require('./launchers/retry').decoratorFactory
|
||||
const processDecorator = require('./launchers/process').decoratorFactory
|
||||
|
||||
// TODO(vojta): remove once nobody uses it
|
||||
const baseBrowserDecoratorFactory = function (
|
||||
baseLauncherDecorator,
|
||||
captureTimeoutLauncherDecorator,
|
||||
retryLauncherDecorator,
|
||||
processLauncherDecorator,
|
||||
processKillTimeout
|
||||
) {
|
||||
return function (launcher) {
|
||||
baseLauncherDecorator(launcher)
|
||||
captureTimeoutLauncherDecorator(launcher)
|
||||
retryLauncherDecorator(launcher)
|
||||
processLauncherDecorator(launcher, processKillTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
class Launcher {
|
||||
constructor (server, emitter, injector) {
|
||||
this._server = server
|
||||
this._emitter = emitter
|
||||
this._injector = injector
|
||||
this._browsers = []
|
||||
this._lastStartTime = null
|
||||
|
||||
// Attach list of dependency injection parameters to methods.
|
||||
this.launch.$inject = [
|
||||
'config.browsers',
|
||||
'config.concurrency'
|
||||
]
|
||||
|
||||
this.launchSingle.$inject = [
|
||||
'config.protocol',
|
||||
'config.hostname',
|
||||
'config.port',
|
||||
'config.urlRoot',
|
||||
'config.upstreamProxy',
|
||||
'config.processKillTimeout'
|
||||
]
|
||||
|
||||
this._emitter.on('exit', (callback) => this.killAll(callback))
|
||||
}
|
||||
|
||||
getBrowserById (id) {
|
||||
return this._browsers.find((browser) => browser.id === id)
|
||||
}
|
||||
|
||||
launchSingle (protocol, hostname, port, urlRoot, upstreamProxy, processKillTimeout) {
|
||||
if (upstreamProxy) {
|
||||
protocol = upstreamProxy.protocol
|
||||
hostname = upstreamProxy.hostname
|
||||
port = upstreamProxy.port
|
||||
urlRoot = upstreamProxy.path + urlRoot.slice(1)
|
||||
}
|
||||
|
||||
return (name) => {
|
||||
let browser
|
||||
const locals = {
|
||||
id: ['value', Launcher.generateId()],
|
||||
name: ['value', name],
|
||||
processKillTimeout: ['value', processKillTimeout],
|
||||
baseLauncherDecorator: ['factory', baseDecorator],
|
||||
captureTimeoutLauncherDecorator: ['factory', captureTimeoutDecorator],
|
||||
retryLauncherDecorator: ['factory', retryDecorator],
|
||||
processLauncherDecorator: ['factory', processDecorator],
|
||||
baseBrowserDecorator: ['factory', baseBrowserDecoratorFactory]
|
||||
}
|
||||
|
||||
// TODO(vojta): determine script from name
|
||||
if (name.includes('/')) {
|
||||
name = 'Script'
|
||||
}
|
||||
|
||||
try {
|
||||
browser = this._injector.createChild([locals], ['launcher:' + name]).get('launcher:' + name)
|
||||
} catch (e) {
|
||||
if (e.message.includes(`No provider for "launcher:${name}"`)) {
|
||||
log.error(`Cannot load browser "${name}": it is not registered! Perhaps you are missing some plugin?`)
|
||||
} else {
|
||||
log.error(`Cannot load browser "${name}"!\n ` + e.stack)
|
||||
}
|
||||
|
||||
this._emitter.emit('load_error', 'launcher', name)
|
||||
return
|
||||
}
|
||||
|
||||
this.jobs.add((args, done) => {
|
||||
log.info(`Starting browser ${browser.displayName || browser.name}`)
|
||||
|
||||
browser.on('browser_process_failure', () => done(browser.error))
|
||||
|
||||
browser.on('done', () => {
|
||||
if (!browser.error && browser.state !== browser.STATE_RESTARTING) {
|
||||
done(null, browser)
|
||||
}
|
||||
})
|
||||
|
||||
browser.start(`${protocol}//${hostname}:${port}${urlRoot}`)
|
||||
}, [])
|
||||
|
||||
this.jobs.run()
|
||||
this._browsers.push(browser)
|
||||
}
|
||||
}
|
||||
|
||||
launch (names, concurrency) {
|
||||
log.info(`Launching browsers ${names.join(', ')} with concurrency ${concurrency === Infinity ? 'unlimited' : concurrency}`)
|
||||
this.jobs = new Jobs({ maxConcurrency: concurrency })
|
||||
|
||||
this._lastStartTime = Date.now()
|
||||
|
||||
if (this._server.loadErrors.length) {
|
||||
this.jobs.add((args, done) => done(), [])
|
||||
} else {
|
||||
names.forEach((name) => this._injector.invoke(this.launchSingle, this)(name))
|
||||
}
|
||||
|
||||
this.jobs.on('end', (err) => {
|
||||
log.debug('Finished all browsers')
|
||||
|
||||
if (err) {
|
||||
log.error(err)
|
||||
}
|
||||
})
|
||||
|
||||
this.jobs.run()
|
||||
|
||||
return this._browsers
|
||||
}
|
||||
|
||||
kill (id, callback) {
|
||||
callback = callback || function () {}
|
||||
const browser = this.getBrowserById(id)
|
||||
|
||||
if (browser) {
|
||||
browser.forceKill().then(callback)
|
||||
return true
|
||||
}
|
||||
process.nextTick(callback)
|
||||
return false
|
||||
}
|
||||
|
||||
restart (id) {
|
||||
const browser = this.getBrowserById(id)
|
||||
if (browser) {
|
||||
browser.restart()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
killAll (callback) {
|
||||
callback = callback || function () {}
|
||||
log.debug('Disconnecting all browsers')
|
||||
|
||||
if (!this._browsers.length) {
|
||||
return process.nextTick(callback)
|
||||
}
|
||||
|
||||
Promise.all(
|
||||
this._browsers
|
||||
.map((browser) => browser.forceKill())
|
||||
).then(callback)
|
||||
}
|
||||
|
||||
areAllCaptured () {
|
||||
return this._browsers.every((browser) => browser.isCaptured())
|
||||
}
|
||||
|
||||
markCaptured (id) {
|
||||
const browser = this.getBrowserById(id)
|
||||
if (browser) {
|
||||
browser.markCaptured()
|
||||
log.debug(`${browser.name} (id ${browser.id}) captured in ${(Date.now() - this._lastStartTime) / 1000} secs`)
|
||||
}
|
||||
}
|
||||
|
||||
static generateId () {
|
||||
return Math.floor(Math.random() * 100000000).toString()
|
||||
}
|
||||
}
|
||||
|
||||
Launcher.factory = function (server, emitter, injector) {
|
||||
return new Launcher(server, emitter, injector)
|
||||
}
|
||||
|
||||
Launcher.factory.$inject = ['server', 'emitter', 'injector']
|
||||
|
||||
exports.Launcher = Launcher
|
140
my-app/node_modules/karma/lib/launchers/base.js
generated
vendored
Executable file
140
my-app/node_modules/karma/lib/launchers/base.js
generated
vendored
Executable file
|
@ -0,0 +1,140 @@
|
|||
const KarmaEventEmitter = require('../events').EventEmitter
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
|
||||
const log = require('../logger').create('launcher')
|
||||
const helper = require('../helper')
|
||||
|
||||
const BEING_CAPTURED = 'BEING_CAPTURED'
|
||||
const CAPTURED = 'CAPTURED'
|
||||
const BEING_KILLED = 'BEING_KILLED'
|
||||
const FINISHED = 'FINISHED'
|
||||
const RESTARTING = 'RESTARTING'
|
||||
const BEING_FORCE_KILLED = 'BEING_FORCE_KILLED'
|
||||
|
||||
/**
|
||||
* Base launcher that any custom launcher extends.
|
||||
*/
|
||||
function BaseLauncher (id, emitter) {
|
||||
if (this.start) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(vojta): figure out how to do inheritance with DI
|
||||
Object.keys(EventEmitter.prototype).forEach(function (method) {
|
||||
this[method] = EventEmitter.prototype[method]
|
||||
}, this)
|
||||
|
||||
this.bind = KarmaEventEmitter.prototype.bind.bind(this)
|
||||
this.emitAsync = KarmaEventEmitter.prototype.emitAsync.bind(this)
|
||||
|
||||
this.id = id
|
||||
this._state = null
|
||||
Object.defineProperty(this, 'state', {
|
||||
get: () => {
|
||||
return this._state
|
||||
},
|
||||
set: (toState) => {
|
||||
log.debug(`${this._state} -> ${toState}`)
|
||||
this._state = toState
|
||||
}
|
||||
})
|
||||
|
||||
this.error = null
|
||||
|
||||
let killingPromise
|
||||
let previousUrl
|
||||
|
||||
this.start = function (url) {
|
||||
previousUrl = url
|
||||
|
||||
this.error = null
|
||||
this.state = BEING_CAPTURED
|
||||
this.emit('start', url + '?id=' + this.id + (helper.isDefined(this.displayName) ? '&displayName=' + encodeURIComponent(this.displayName) : ''))
|
||||
}
|
||||
|
||||
this.kill = function () {
|
||||
// Already killed, or being killed.
|
||||
if (killingPromise) {
|
||||
return killingPromise
|
||||
}
|
||||
|
||||
killingPromise = this.emitAsync('kill').then(() => {
|
||||
this.state = FINISHED
|
||||
})
|
||||
|
||||
this.state = BEING_KILLED
|
||||
|
||||
return killingPromise
|
||||
}
|
||||
|
||||
this.forceKill = function () {
|
||||
this.kill()
|
||||
this.state = BEING_FORCE_KILLED
|
||||
|
||||
return killingPromise
|
||||
}
|
||||
|
||||
this.restart = function () {
|
||||
if (this.state === BEING_FORCE_KILLED) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!killingPromise) {
|
||||
killingPromise = this.emitAsync('kill')
|
||||
}
|
||||
|
||||
killingPromise.then(() => {
|
||||
if (this.state === BEING_FORCE_KILLED) {
|
||||
this.state = FINISHED
|
||||
} else {
|
||||
killingPromise = null
|
||||
log.debug(`Restarting ${this.name}`)
|
||||
this.start(previousUrl)
|
||||
}
|
||||
})
|
||||
|
||||
this.state = RESTARTING
|
||||
}
|
||||
|
||||
this.markCaptured = function () {
|
||||
if (this.state === BEING_CAPTURED) {
|
||||
this.state = CAPTURED
|
||||
}
|
||||
}
|
||||
|
||||
this.isCaptured = function () {
|
||||
return this.state === CAPTURED
|
||||
}
|
||||
|
||||
this.toString = function () {
|
||||
return this.name
|
||||
}
|
||||
|
||||
this._done = function (error) {
|
||||
killingPromise = killingPromise || Promise.resolve()
|
||||
|
||||
this.error = this.error || error
|
||||
this.emit('done')
|
||||
|
||||
if (this.error && this.state !== BEING_FORCE_KILLED && this.state !== RESTARTING) {
|
||||
emitter.emit('browser_process_failure', this)
|
||||
}
|
||||
|
||||
this.state = FINISHED
|
||||
}
|
||||
|
||||
this.STATE_BEING_CAPTURED = BEING_CAPTURED
|
||||
this.STATE_CAPTURED = CAPTURED
|
||||
this.STATE_BEING_KILLED = BEING_KILLED
|
||||
this.STATE_FINISHED = FINISHED
|
||||
this.STATE_RESTARTING = RESTARTING
|
||||
this.STATE_BEING_FORCE_KILLED = BEING_FORCE_KILLED
|
||||
}
|
||||
|
||||
BaseLauncher.decoratorFactory = function (id, emitter) {
|
||||
return function (launcher) {
|
||||
BaseLauncher.call(launcher, id, emitter)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseLauncher
|
43
my-app/node_modules/karma/lib/launchers/capture_timeout.js
generated
vendored
Executable file
43
my-app/node_modules/karma/lib/launchers/capture_timeout.js
generated
vendored
Executable file
|
@ -0,0 +1,43 @@
|
|||
const log = require('../logger').create('launcher')
|
||||
|
||||
/**
|
||||
* Kill browser if it does not capture in given `captureTimeout`.
|
||||
*/
|
||||
function CaptureTimeoutLauncher (timer, captureTimeout) {
|
||||
if (!captureTimeout) {
|
||||
return
|
||||
}
|
||||
|
||||
let pendingTimeoutId = null
|
||||
|
||||
this.on('start', () => {
|
||||
pendingTimeoutId = timer.setTimeout(() => {
|
||||
pendingTimeoutId = null
|
||||
if (this.state !== this.STATE_BEING_CAPTURED) {
|
||||
return
|
||||
}
|
||||
|
||||
log.warn(`${this.name} has not captured in ${captureTimeout} ms, killing.`)
|
||||
this.error = 'timeout'
|
||||
this.kill()
|
||||
}, captureTimeout)
|
||||
})
|
||||
|
||||
this.on('done', () => {
|
||||
if (pendingTimeoutId) {
|
||||
timer.clearTimeout(pendingTimeoutId)
|
||||
pendingTimeoutId = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
CaptureTimeoutLauncher.decoratorFactory = function (timer,
|
||||
/* config.captureTimeout */ captureTimeout) {
|
||||
return function (launcher) {
|
||||
CaptureTimeoutLauncher.call(launcher, timer, captureTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
CaptureTimeoutLauncher.decoratorFactory.$inject = ['timer', 'config.captureTimeout']
|
||||
|
||||
module.exports = CaptureTimeoutLauncher
|
185
my-app/node_modules/karma/lib/launchers/process.js
generated
vendored
Executable file
185
my-app/node_modules/karma/lib/launchers/process.js
generated
vendored
Executable file
|
@ -0,0 +1,185 @@
|
|||
const path = require('path')
|
||||
const log = require('../logger').create('launcher')
|
||||
const env = process.env
|
||||
|
||||
function ProcessLauncher (spawn, tempDir, timer, processKillTimeout) {
|
||||
const self = this
|
||||
let onExitCallback
|
||||
const killTimeout = processKillTimeout || 2000
|
||||
// Will hold output from the spawned child process
|
||||
const streamedOutputs = {
|
||||
stdout: '',
|
||||
stderr: ''
|
||||
}
|
||||
|
||||
this._tempDir = tempDir.getPath(`/karma-${this.id.toString()}`)
|
||||
|
||||
this.on('start', function (url) {
|
||||
tempDir.create(self._tempDir)
|
||||
self._start(url)
|
||||
})
|
||||
|
||||
this.on('kill', function (done) {
|
||||
if (!self._process) {
|
||||
return process.nextTick(done)
|
||||
}
|
||||
|
||||
onExitCallback = done
|
||||
self._process.kill()
|
||||
self._killTimer = timer.setTimeout(self._onKillTimeout, killTimeout)
|
||||
})
|
||||
|
||||
this._start = function (url) {
|
||||
self._execCommand(self._getCommand(), self._getOptions(url))
|
||||
}
|
||||
|
||||
this._getCommand = function () {
|
||||
return env[self.ENV_CMD] || self.DEFAULT_CMD[process.platform]
|
||||
}
|
||||
|
||||
this._getOptions = function (url) {
|
||||
return [url]
|
||||
}
|
||||
|
||||
// Normalize the command, remove quotes (spawn does not like them).
|
||||
this._normalizeCommand = function (cmd) {
|
||||
if (cmd.charAt(0) === cmd.charAt(cmd.length - 1) && '\'`"'.includes(cmd.charAt(0))) {
|
||||
cmd = cmd.slice(1, -1)
|
||||
log.warn(`The path should not be quoted.\n Normalized the path to ${cmd}`)
|
||||
}
|
||||
|
||||
return path.normalize(cmd)
|
||||
}
|
||||
|
||||
this._onStdout = function (data) {
|
||||
streamedOutputs.stdout += data
|
||||
}
|
||||
|
||||
this._onStderr = function (data) {
|
||||
streamedOutputs.stderr += data
|
||||
}
|
||||
|
||||
this._execCommand = function (cmd, args) {
|
||||
if (!cmd) {
|
||||
log.error(`No binary for ${self.name} browser on your platform.\n Please, set "${self.ENV_CMD}" env variable.`)
|
||||
|
||||
// disable restarting
|
||||
self._retryLimit = -1
|
||||
|
||||
return self._clearTempDirAndReportDone('no binary')
|
||||
}
|
||||
|
||||
cmd = this._normalizeCommand(cmd)
|
||||
|
||||
log.debug(cmd + ' ' + args.join(' '))
|
||||
self._process = spawn(cmd, args)
|
||||
let errorOutput = ''
|
||||
|
||||
self._process.stdout.on('data', self._onStdout)
|
||||
|
||||
self._process.stderr.on('data', self._onStderr)
|
||||
|
||||
self._process.on('exit', function (code, signal) {
|
||||
self._onProcessExit(code, signal, errorOutput)
|
||||
})
|
||||
|
||||
self._process.on('error', function (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
self._retryLimit = -1
|
||||
errorOutput = `Can not find the binary ${cmd}\n\tPlease set env variable ${self.ENV_CMD}`
|
||||
} else if (err.code === 'EACCES') {
|
||||
self._retryLimit = -1
|
||||
errorOutput = `Permission denied accessing the binary ${cmd}\n\tMaybe it's a directory?`
|
||||
} else {
|
||||
errorOutput += err.toString()
|
||||
}
|
||||
self._onProcessExit(-1, null, errorOutput)
|
||||
})
|
||||
|
||||
self._process.stderr.on('data', function (errBuff) {
|
||||
errorOutput += errBuff.toString()
|
||||
})
|
||||
}
|
||||
|
||||
this._onProcessExit = function (code, signal, errorOutput) {
|
||||
if (!self._process) {
|
||||
// Both exit and error events trigger _onProcessExit(), but we only need one cleanup.
|
||||
return
|
||||
}
|
||||
log.debug(`Process ${self.name} exited with code ${code} and signal ${signal}`)
|
||||
|
||||
let error = null
|
||||
|
||||
if (self.state === self.STATE_BEING_CAPTURED) {
|
||||
log.error(`Cannot start ${self.name}\n\t${errorOutput}`)
|
||||
error = 'cannot start'
|
||||
}
|
||||
|
||||
if (self.state === self.STATE_CAPTURED) {
|
||||
log.error(`${self.name} crashed.\n\t${errorOutput}`)
|
||||
error = 'crashed'
|
||||
}
|
||||
|
||||
if (error) {
|
||||
log.error(`${self.name} stdout: ${streamedOutputs.stdout}`)
|
||||
log.error(`${self.name} stderr: ${streamedOutputs.stderr}`)
|
||||
}
|
||||
|
||||
self._process = null
|
||||
streamedOutputs.stdout = ''
|
||||
streamedOutputs.stderr = ''
|
||||
if (self._killTimer) {
|
||||
timer.clearTimeout(self._killTimer)
|
||||
self._killTimer = null
|
||||
}
|
||||
self._clearTempDirAndReportDone(error)
|
||||
}
|
||||
|
||||
this._clearTempDirAndReportDone = function (error) {
|
||||
tempDir.remove(self._tempDir, function () {
|
||||
self._done(error)
|
||||
if (onExitCallback) {
|
||||
onExitCallback()
|
||||
onExitCallback = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this._onKillTimeout = function () {
|
||||
if (self.state !== self.STATE_BEING_KILLED && self.state !== self.STATE_BEING_FORCE_KILLED) {
|
||||
return
|
||||
}
|
||||
|
||||
log.warn(`${self.name} was not killed in ${killTimeout} ms, sending SIGKILL.`)
|
||||
self._process.kill('SIGKILL')
|
||||
|
||||
// NOTE: https://github.com/karma-runner/karma/pull/1184
|
||||
// NOTE: SIGKILL is just a signal. Processes should never ignore it, but they can.
|
||||
// If a process gets into a state where it doesn't respond in a reasonable amount of time
|
||||
// Karma should warn, and continue as though the kill succeeded.
|
||||
// This a certainly suboptimal, but it is better than having the test harness hang waiting
|
||||
// for a zombie child process to exit.
|
||||
self._killTimer = timer.setTimeout(function () {
|
||||
log.warn(`${self.name} was not killed by SIGKILL in ${killTimeout} ms, continuing.`)
|
||||
self._onProcessExit(-1, null, '')
|
||||
}, killTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
ProcessLauncher.decoratorFactory = function (timer) {
|
||||
return function (launcher, processKillTimeout) {
|
||||
const spawn = require('child_process').spawn
|
||||
|
||||
function spawnWithoutOutput () {
|
||||
const proc = spawn.apply(null, arguments)
|
||||
proc.stdout.resume()
|
||||
proc.stderr.resume()
|
||||
|
||||
return proc
|
||||
}
|
||||
|
||||
ProcessLauncher.call(launcher, spawnWithoutOutput, require('../temp_dir'), timer, processKillTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProcessLauncher
|
31
my-app/node_modules/karma/lib/launchers/retry.js
generated
vendored
Executable file
31
my-app/node_modules/karma/lib/launchers/retry.js
generated
vendored
Executable file
|
@ -0,0 +1,31 @@
|
|||
const log = require('../logger').create('launcher')
|
||||
|
||||
function RetryLauncher (retryLimit) {
|
||||
this._retryLimit = retryLimit
|
||||
|
||||
this.on('done', () => {
|
||||
if (!this.error) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._retryLimit > 0) {
|
||||
log.info(`Trying to start ${this.name} again (${retryLimit - this._retryLimit + 1}/${retryLimit}).`)
|
||||
this.restart()
|
||||
this._retryLimit--
|
||||
} else if (this._retryLimit === 0) {
|
||||
log.error(`${this.name} failed ${retryLimit} times (${this.error}). Giving up.`)
|
||||
} else {
|
||||
log.debug(`${this.name} failed (${this.error}). Not restarting.`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
RetryLauncher.decoratorFactory = function (retryLimit) {
|
||||
return function (launcher) {
|
||||
RetryLauncher.call(launcher, retryLimit)
|
||||
}
|
||||
}
|
||||
|
||||
RetryLauncher.decoratorFactory.$inject = ['config.retryLimit']
|
||||
|
||||
module.exports = RetryLauncher
|
115
my-app/node_modules/karma/lib/logger.js
generated
vendored
Executable file
115
my-app/node_modules/karma/lib/logger.js
generated
vendored
Executable file
|
@ -0,0 +1,115 @@
|
|||
// This is the **logger** module for *Karma*. It uses
|
||||
// [log4js](https://github.com/nomiddlename/log4js-node) to handle and
|
||||
// configure all logging that happens inside of *Karma*.
|
||||
|
||||
// ### Helpers and Setup
|
||||
|
||||
let log4js = require('log4js')
|
||||
const helper = require('./helper')
|
||||
const constant = require('./constants')
|
||||
|
||||
// #### Public Functions
|
||||
|
||||
// Setup the logger by passing in the configuration options. It needs
|
||||
// three arguments:
|
||||
//
|
||||
// setup(logLevel, colors, appenders)
|
||||
//
|
||||
// * `logLevel`: *String* Defines the global log level.
|
||||
// * `colors`: *Boolean* Use colors in the stdout or not.
|
||||
// * `appenders`: *Object* This will be passed as appenders to log4js
|
||||
// to allow for fine grained configuration of log4js. For more information
|
||||
// see https://github.com/nomiddlename/log4js-node.
|
||||
// *Array* is also accepted for backwards compatibility.
|
||||
function setup (level, colors, appenders) {
|
||||
// Turn color on/off on the console appenders with pattern layout
|
||||
const pattern = colors ? constant.COLOR_PATTERN : constant.NO_COLOR_PATTERN
|
||||
if (appenders) {
|
||||
// Convert Array to Object for backwards compatibility.
|
||||
if (appenders.map) {
|
||||
if (appenders.length === 0) {
|
||||
appenders = [constant.CONSOLE_APPENDER]
|
||||
}
|
||||
const v1Appenders = appenders
|
||||
appenders = {}
|
||||
v1Appenders.forEach(function (appender, index) {
|
||||
if (appender.type === 'console') {
|
||||
appenders.console = appender
|
||||
if (helper.isDefined(appender.layout) && appender.layout.type === 'pattern') {
|
||||
appender.layout.pattern = pattern
|
||||
}
|
||||
} else {
|
||||
appenders[index + ''] = appender
|
||||
}
|
||||
return appender
|
||||
})
|
||||
}
|
||||
} else {
|
||||
appenders = { console: constant.CONSOLE_APPENDER }
|
||||
}
|
||||
|
||||
log4js.configure({
|
||||
appenders: appenders,
|
||||
categories: {
|
||||
default: {
|
||||
appenders: Object.keys(appenders),
|
||||
level: level
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Setup the logger by passing in the config object. The function sets the
|
||||
// `colors` and `logLevel` if they are defined. It takes two arguments:
|
||||
//
|
||||
// setupFromConfig(config, appenders)
|
||||
//
|
||||
// * `config`: *Object* The configuration object.
|
||||
// * `appenders`: *Object* This will be passed as appenders to log4js
|
||||
// to allow for fine grained configuration of log4js. For more information
|
||||
// see https://github.com/nomiddlename/log4js-node.
|
||||
// *Array* is also accepted for backwards compatibility.
|
||||
function setupFromConfig (config, appenders) {
|
||||
let useColors = true
|
||||
let logLevel = constant.LOG_INFO
|
||||
|
||||
if (helper.isDefined(config.colors)) {
|
||||
useColors = config.colors
|
||||
}
|
||||
|
||||
if (helper.isDefined(config.logLevel)) {
|
||||
logLevel = config.logLevel
|
||||
}
|
||||
setup(logLevel, useColors, appenders)
|
||||
}
|
||||
|
||||
const loggerCache = {}
|
||||
|
||||
// Create a new logger. There are two optional arguments
|
||||
// * `name`, which defaults to `karma` and
|
||||
// If the `name = 'socket.io'` this will create a special wrapper
|
||||
// to be used as a logger for socket.io.
|
||||
// * `level`, which defaults to the global level.
|
||||
function create (name, level) {
|
||||
name = name || 'karma'
|
||||
let logger
|
||||
if (Object.prototype.hasOwnProperty.call(loggerCache, name)) {
|
||||
logger = loggerCache[name]
|
||||
} else {
|
||||
logger = log4js.getLogger(name)
|
||||
loggerCache[name] = logger
|
||||
}
|
||||
if (helper.isDefined(level)) {
|
||||
logger.setLevel(level)
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
// #### Publish
|
||||
|
||||
exports.create = create
|
||||
exports.setup = setup
|
||||
exports.setupFromConfig = setupFromConfig
|
||||
exports._rebindLog4js4testing = function (mockLog4js) {
|
||||
log4js = mockLog4js
|
||||
}
|
105
my-app/node_modules/karma/lib/middleware/common.js
generated
vendored
Executable file
105
my-app/node_modules/karma/lib/middleware/common.js
generated
vendored
Executable file
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* This module contains some common helpers shared between middlewares
|
||||
*/
|
||||
'use strict'
|
||||
|
||||
const mime = require('mime')
|
||||
const parseRange = require('range-parser')
|
||||
const log = require('../logger').create('web-server')
|
||||
|
||||
function createServeFile (fs, directory, config) {
|
||||
const cache = Object.create(null)
|
||||
|
||||
return function (filepath, rangeHeader, response, transform, content, doNotCache) {
|
||||
let responseData
|
||||
|
||||
function convertForRangeRequest () {
|
||||
const range = parseRange(responseData.length, rangeHeader)
|
||||
if (range === -2) {
|
||||
return 200 // malformed header string
|
||||
} else if (range === -1) {
|
||||
responseData = Buffer.alloc(0) // unsatisfiable range
|
||||
return 416
|
||||
} else if (range.type === 'bytes') {
|
||||
responseData = Buffer.from(responseData)
|
||||
if (range.length === 1) {
|
||||
const { start, end } = range[0]
|
||||
response.setHeader('Content-Range', `bytes ${start}-${end}/${responseData.length}`)
|
||||
response.setHeader('Accept-Ranges', 'bytes')
|
||||
response.setHeader('Content-Length', end - start + 1)
|
||||
responseData = responseData.slice(start, end + 1)
|
||||
return 206
|
||||
} else {
|
||||
responseData = Buffer.alloc(0) // Multiple ranges are not supported. Maybe future?
|
||||
return 416
|
||||
}
|
||||
}
|
||||
return 200 // All other states, ignore
|
||||
}
|
||||
|
||||
if (directory) {
|
||||
filepath = directory + filepath
|
||||
}
|
||||
|
||||
if (!content && cache[filepath]) {
|
||||
content = cache[filepath]
|
||||
}
|
||||
|
||||
if (config && config.customHeaders && config.customHeaders.length > 0) {
|
||||
config.customHeaders.forEach((header) => {
|
||||
const regex = new RegExp(header.match)
|
||||
if (regex.test(filepath)) {
|
||||
log.debug(`setting header: ${header.name} for: ${filepath}`)
|
||||
response.setHeader(header.name, header.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (content && !doNotCache) {
|
||||
log.debug(`serving (cached): ${filepath}`)
|
||||
response.setHeader('Content-Type', mime.getType(filepath, 'text/plain'))
|
||||
responseData = (transform && transform(content)) || content
|
||||
response.writeHead(rangeHeader ? convertForRangeRequest() : 200)
|
||||
return response.end(responseData)
|
||||
}
|
||||
|
||||
return fs.readFile(filepath, function (error, data) {
|
||||
if (error) {
|
||||
return serve404(response, filepath)
|
||||
}
|
||||
|
||||
if (!doNotCache) {
|
||||
cache[filepath] = data.toString()
|
||||
}
|
||||
|
||||
log.debug('serving: ' + filepath)
|
||||
response.setHeader('Content-Type', mime.getType(filepath, 'text/plain'))
|
||||
responseData = (transform && transform(data.toString())) || data
|
||||
response.writeHead(rangeHeader ? convertForRangeRequest() : 200)
|
||||
|
||||
return response.end(responseData)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function serve404 (response, path) {
|
||||
log.warn(`404: ${path}`)
|
||||
response.writeHead(404)
|
||||
return response.end('NOT FOUND')
|
||||
}
|
||||
|
||||
function setNoCacheHeaders (response) {
|
||||
response.setHeader('Cache-Control', 'no-cache')
|
||||
response.setHeader('Pragma', 'no-cache')
|
||||
response.setHeader('Expires', (new Date(0)).toUTCString())
|
||||
}
|
||||
|
||||
function setHeavyCacheHeaders (response) {
|
||||
response.setHeader('Cache-Control', 'public, max-age=31536000')
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
exports.createServeFile = createServeFile
|
||||
exports.setNoCacheHeaders = setNoCacheHeaders
|
||||
exports.setHeavyCacheHeaders = setHeavyCacheHeaders
|
||||
exports.serve404 = serve404
|
259
my-app/node_modules/karma/lib/middleware/karma.js
generated
vendored
Executable file
259
my-app/node_modules/karma/lib/middleware/karma.js
generated
vendored
Executable file
|
@ -0,0 +1,259 @@
|
|||
/**
|
||||
* Karma middleware is responsible for serving:
|
||||
* - client.html (the entrypoint for capturing a browser)
|
||||
* - debug.html
|
||||
* - context.html (the execution context, loaded within an iframe)
|
||||
* - karma.js
|
||||
*
|
||||
* The main part is generating context.html, as it contains:
|
||||
* - generating mappings
|
||||
* - including <script> and <link> tags
|
||||
* - setting propert caching headers
|
||||
*/
|
||||
|
||||
const url = require('url')
|
||||
|
||||
const log = require('../logger').create('middleware:karma')
|
||||
const stripHost = require('./strip_host').stripHost
|
||||
const common = require('./common')
|
||||
|
||||
const VERSION = require('../constants').VERSION
|
||||
const SCRIPT_TYPE = {
|
||||
js: 'text/javascript',
|
||||
module: 'module'
|
||||
}
|
||||
const FILE_TYPES = [
|
||||
'css',
|
||||
'html',
|
||||
'js',
|
||||
'module',
|
||||
'dom'
|
||||
]
|
||||
|
||||
function filePathToUrlPath (filePath, basePath, urlRoot, proxyPath) {
|
||||
if (filePath.startsWith(basePath)) {
|
||||
return proxyPath + urlRoot.slice(1) + 'base' + filePath.slice(basePath.length)
|
||||
}
|
||||
return proxyPath + urlRoot.slice(1) + 'absolute' + filePath
|
||||
}
|
||||
|
||||
function getQuery (urlStr) {
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
return url.parse(urlStr, true).query || {}
|
||||
}
|
||||
|
||||
function getXUACompatibleMetaElement (url) {
|
||||
const query = getQuery(url)
|
||||
if (query['x-ua-compatible']) {
|
||||
return `<meta http-equiv="X-UA-Compatible" content="${query['x-ua-compatible']}"/>`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function getXUACompatibleUrl (url) {
|
||||
const query = getQuery(url)
|
||||
if (query['x-ua-compatible']) {
|
||||
return '?x-ua-compatible=' + encodeURIComponent(query['x-ua-compatible'])
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function createKarmaMiddleware (
|
||||
filesPromise,
|
||||
serveStaticFile,
|
||||
serveFile,
|
||||
injector,
|
||||
basePath,
|
||||
urlRoot,
|
||||
upstreamProxy,
|
||||
browserSocketTimeout
|
||||
) {
|
||||
const proxyPath = upstreamProxy ? upstreamProxy.path : '/'
|
||||
return function (request, response, next) {
|
||||
// These config values should be up to date on every request
|
||||
const client = injector.get('config.client')
|
||||
const customContextFile = injector.get('config.customContextFile')
|
||||
const customDebugFile = injector.get('config.customDebugFile')
|
||||
const customClientContextFile = injector.get('config.customClientContextFile')
|
||||
const includeCrossOriginAttribute = injector.get('config.crossOriginAttribute')
|
||||
|
||||
const normalizedUrl = stripHost(request.url) || request.url
|
||||
// For backwards compatibility in middleware plugins, remove in v4.
|
||||
request.normalizedUrl = normalizedUrl
|
||||
|
||||
let requestUrl = normalizedUrl.replace(/\?.*/, '')
|
||||
const requestedRangeHeader = request.headers.range
|
||||
|
||||
// redirect /__karma__ to /__karma__ (trailing slash)
|
||||
if (requestUrl === urlRoot.slice(0, -1)) {
|
||||
response.setHeader('Location', proxyPath + urlRoot.slice(1))
|
||||
response.writeHead(301)
|
||||
return response.end('MOVED PERMANENTLY')
|
||||
}
|
||||
|
||||
// ignore urls outside urlRoot
|
||||
if (!requestUrl.startsWith(urlRoot)) {
|
||||
return next()
|
||||
}
|
||||
|
||||
// remove urlRoot prefix
|
||||
requestUrl = requestUrl.slice(urlRoot.length - 1)
|
||||
|
||||
// serve client.html
|
||||
if (requestUrl === '/') {
|
||||
// redirect client_with_context.html
|
||||
if (!client.useIframe && client.runInParent) {
|
||||
requestUrl = '/client_with_context.html'
|
||||
} else { // serve client.html
|
||||
return serveStaticFile('/client.html', requestedRangeHeader, response, (data) =>
|
||||
data
|
||||
.replace('%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
|
||||
.replace('%X_UA_COMPATIBLE_URL%', getXUACompatibleUrl(request.url)))
|
||||
}
|
||||
}
|
||||
|
||||
if (['/karma.js', '/context.js', '/debug.js'].includes(requestUrl)) {
|
||||
return serveStaticFile(requestUrl, requestedRangeHeader, response, (data) =>
|
||||
data
|
||||
.replace('%KARMA_URL_ROOT%', urlRoot)
|
||||
.replace('%KARMA_VERSION%', VERSION)
|
||||
.replace('%KARMA_PROXY_PATH%', proxyPath)
|
||||
.replace('%BROWSER_SOCKET_TIMEOUT%', browserSocketTimeout))
|
||||
}
|
||||
|
||||
// serve the favicon
|
||||
if (requestUrl === '/favicon.ico') {
|
||||
return serveStaticFile(requestUrl, requestedRangeHeader, response)
|
||||
}
|
||||
|
||||
// serve context.html - execution context within the iframe
|
||||
// or debug.html - execution context without channel to the server
|
||||
const isRequestingContextFile = requestUrl === '/context.html'
|
||||
const isRequestingDebugFile = requestUrl === '/debug.html'
|
||||
const isRequestingClientContextFile = requestUrl === '/client_with_context.html'
|
||||
if (isRequestingContextFile || isRequestingDebugFile || isRequestingClientContextFile) {
|
||||
return filesPromise.then((files) => {
|
||||
let fileServer
|
||||
let requestedFileUrl
|
||||
log.debug('custom files', customContextFile, customDebugFile, customClientContextFile)
|
||||
if (isRequestingContextFile && customContextFile) {
|
||||
log.debug(`Serving customContextFile ${customContextFile}`)
|
||||
fileServer = serveFile
|
||||
requestedFileUrl = customContextFile
|
||||
} else if (isRequestingDebugFile && customDebugFile) {
|
||||
log.debug(`Serving customDebugFile ${customDebugFile}`)
|
||||
fileServer = serveFile
|
||||
requestedFileUrl = customDebugFile
|
||||
} else if (isRequestingClientContextFile && customClientContextFile) {
|
||||
log.debug(`Serving customClientContextFile ${customClientContextFile}`)
|
||||
fileServer = serveFile
|
||||
requestedFileUrl = customClientContextFile
|
||||
} else {
|
||||
log.debug(`Serving static request ${requestUrl}`)
|
||||
fileServer = serveStaticFile
|
||||
requestedFileUrl = requestUrl
|
||||
}
|
||||
|
||||
fileServer(requestedFileUrl, requestedRangeHeader, response, function (data) {
|
||||
common.setNoCacheHeaders(response)
|
||||
|
||||
const scriptTags = []
|
||||
for (const file of files.included) {
|
||||
let filePath = file.path
|
||||
const fileType = file.type || file.detectType()
|
||||
|
||||
if (!FILE_TYPES.includes(fileType)) {
|
||||
if (file.type == null) {
|
||||
log.warn(
|
||||
'Unable to determine file type from the file extension, defaulting to js.\n' +
|
||||
` To silence the warning specify a valid type for ${file.originalPath} in the configuration file.\n` +
|
||||
' See https://karma-runner.github.io/latest/config/files.html'
|
||||
)
|
||||
} else {
|
||||
log.warn(`Invalid file type (${file.type || 'empty string'}), defaulting to js.`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.isUrl) {
|
||||
filePath = filePathToUrlPath(filePath, basePath, urlRoot, proxyPath)
|
||||
|
||||
if (requestUrl === '/context.html') {
|
||||
filePath += '?' + file.sha
|
||||
}
|
||||
}
|
||||
|
||||
const integrityAttribute = file.integrity ? ` integrity="${file.integrity}"` : ''
|
||||
const crossOriginAttribute = includeCrossOriginAttribute ? ' crossorigin="anonymous"' : ''
|
||||
if (fileType === 'css') {
|
||||
scriptTags.push(`<link type="text/css" href="${filePath}" rel="stylesheet"${integrityAttribute}${crossOriginAttribute}>`)
|
||||
} else if (fileType === 'dom') {
|
||||
scriptTags.push(file.content)
|
||||
} else if (fileType === 'html') {
|
||||
scriptTags.push(`<link href="${filePath}" rel="import"${integrityAttribute}${crossOriginAttribute}>`)
|
||||
} else {
|
||||
const scriptType = (SCRIPT_TYPE[fileType] || 'text/javascript')
|
||||
if (fileType === 'module') {
|
||||
scriptTags.push(`<script onerror="throw 'Error loading ${filePath}'" type="${scriptType}" src="${filePath}"${integrityAttribute}${crossOriginAttribute}></script>`)
|
||||
} else {
|
||||
scriptTags.push(`<script type="${scriptType}" src="${filePath}"${integrityAttribute}${crossOriginAttribute}></script>`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scriptUrls = []
|
||||
// For client_with_context, html elements are not added directly through an iframe.
|
||||
// Instead, scriptTags is stored to window.__karma__.scriptUrls first. Later, the
|
||||
// client will read window.__karma__.scriptUrls and dynamically add them to the DOM
|
||||
// using DOMParser.
|
||||
if (requestUrl === '/client_with_context.html') {
|
||||
for (const script of scriptTags) {
|
||||
scriptUrls.push(
|
||||
// Escape characters with special roles (tags) in HTML. Open angle brackets are parsed as tags
|
||||
// immediately, even if it is within double quotations in browsers
|
||||
script.replace(/</g, '\\x3C').replace(/>/g, '\\x3E'))
|
||||
}
|
||||
}
|
||||
|
||||
const mappings = data.includes('%MAPPINGS%') ? files.served.map((file) => {
|
||||
const filePath = filePathToUrlPath(file.path, basePath, urlRoot, proxyPath)
|
||||
.replace(/\\/g, '\\\\') // Windows paths contain backslashes and generate bad IDs if not escaped
|
||||
.replace(/'/g, '\\\'') // Escape single quotes - double quotes should not be allowed!
|
||||
|
||||
return ` '${filePath}': '${file.sha}'`
|
||||
}) : []
|
||||
|
||||
return data
|
||||
.replace('%SCRIPTS%', () => scriptTags.join('\n'))
|
||||
.replace('%CLIENT_CONFIG%', 'window.__karma__.config = ' + JSON.stringify(client) + ';\n')
|
||||
.replace('%SCRIPT_URL_ARRAY%', () => 'window.__karma__.scriptUrls = ' + JSON.stringify(scriptUrls) + ';\n')
|
||||
.replace('%MAPPINGS%', () => 'window.__karma__.files = {\n' + mappings.join(',\n') + '\n};\n')
|
||||
.replace('%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
|
||||
})
|
||||
})
|
||||
} else if (requestUrl === '/context.json') {
|
||||
return filesPromise.then((files) => {
|
||||
common.setNoCacheHeaders(response)
|
||||
response.writeHead(200)
|
||||
response.end(JSON.stringify({
|
||||
files: files.included.map((file) => filePathToUrlPath(file.path + '?' + file.sha, basePath, urlRoot, proxyPath))
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
createKarmaMiddleware.$inject = [
|
||||
'filesPromise',
|
||||
'serveStaticFile',
|
||||
'serveFile',
|
||||
'injector',
|
||||
'config.basePath',
|
||||
'config.urlRoot',
|
||||
'config.upstreamProxy',
|
||||
'config.browserSocketTimeout'
|
||||
]
|
||||
|
||||
// PUBLIC API
|
||||
exports.create = createKarmaMiddleware
|
130
my-app/node_modules/karma/lib/middleware/proxy.js
generated
vendored
Executable file
130
my-app/node_modules/karma/lib/middleware/proxy.js
generated
vendored
Executable file
|
@ -0,0 +1,130 @@
|
|||
const url = require('url')
|
||||
const { Agent: httpAgent } = require('http')
|
||||
const { Agent: httpsAgent } = require('https')
|
||||
const httpProxy = require('http-proxy')
|
||||
const _ = require('lodash')
|
||||
const { lookup } = require('../utils/dns-utils')
|
||||
|
||||
const log = require('../logger').create('proxy')
|
||||
|
||||
function parseProxyConfig (proxies, config) {
|
||||
proxies = proxies || []
|
||||
return _.sortBy(_.map(proxies, function (proxyConfiguration, proxyPath) {
|
||||
if (typeof proxyConfiguration === 'string') {
|
||||
proxyConfiguration = { target: proxyConfiguration }
|
||||
}
|
||||
let proxyUrl = proxyConfiguration.target
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
const proxyDetails = url.parse(proxyUrl)
|
||||
let pathname = proxyDetails.pathname
|
||||
|
||||
if (proxyPath.endsWith('/') && !proxyUrl.endsWith('/')) {
|
||||
log.warn(`proxy "${proxyUrl}" normalized to "${proxyUrl}/"`)
|
||||
proxyUrl += '/'
|
||||
pathname += '/'
|
||||
}
|
||||
|
||||
if (!proxyPath.endsWith('/') && proxyUrl.endsWith('/')) {
|
||||
log.warn(`proxy "${proxyPath}" normalized to "${proxyPath}/"`)
|
||||
proxyPath += '/'
|
||||
}
|
||||
|
||||
if (pathname === '/' && !proxyUrl.endsWith('/')) {
|
||||
pathname = ''
|
||||
}
|
||||
|
||||
const hostname = proxyDetails.hostname || config.hostname
|
||||
const protocol = proxyDetails.protocol || config.protocol
|
||||
const defaultPorts = {
|
||||
'http:': '80',
|
||||
'https:': '443'
|
||||
}
|
||||
const port = proxyDetails.port || defaultPorts[proxyDetails.protocol] || config.port
|
||||
const changeOrigin = proxyConfiguration.changeOrigin || false
|
||||
const Agent = protocol === 'https:' ? httpsAgent : httpAgent
|
||||
const agent = new Agent({
|
||||
keepAlive: true,
|
||||
lookup
|
||||
})
|
||||
const proxy = httpProxy.createProxyServer({
|
||||
target: { host: hostname, port, protocol },
|
||||
xfwd: true,
|
||||
changeOrigin: changeOrigin,
|
||||
secure: config.proxyValidateSSL,
|
||||
agent
|
||||
})
|
||||
|
||||
;['proxyReq', 'proxyRes'].forEach(function (name) {
|
||||
const callback = proxyDetails[name] || config[name]
|
||||
if (callback) {
|
||||
proxy.on(name, callback)
|
||||
}
|
||||
})
|
||||
|
||||
proxy.on('error', function proxyError (err, req, res) {
|
||||
if (err.code === 'ECONNRESET' && req.socket.destroyed) {
|
||||
log.debug(`failed to proxy ${req.url} (browser hung up the socket)`)
|
||||
} else {
|
||||
log.warn(`failed to proxy ${req.url} (${err.message})`)
|
||||
}
|
||||
|
||||
res.destroy()
|
||||
})
|
||||
|
||||
return { path: proxyPath, baseUrl: pathname, host: hostname, port, proxy, agent }
|
||||
}), 'path').reverse()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a handler which understands the proxies and its redirects, along with the proxy to use
|
||||
* @param proxies An array of proxy record objects
|
||||
* @param urlRoot The URL root that karma is mounted on
|
||||
* @return {Function} handler function
|
||||
*/
|
||||
function createProxyHandler (proxies, urlRoot) {
|
||||
if (!proxies.length) {
|
||||
const nullProxy = (request, response, next) => next()
|
||||
nullProxy.upgrade = () => {}
|
||||
return nullProxy
|
||||
}
|
||||
|
||||
function createProxy (request, response, next) {
|
||||
const proxyRecord = proxies.find((p) => request.url.startsWith(p.path))
|
||||
if (proxyRecord) {
|
||||
log.debug(`proxying request - ${request.url} to ${proxyRecord.host}:${proxyRecord.port}`)
|
||||
request.url = request.url.replace(proxyRecord.path, proxyRecord.baseUrl)
|
||||
proxyRecord.proxy.web(request, response)
|
||||
} else {
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
createProxy.upgrade = function (request, socket, head) {
|
||||
// special-case karma's route to avoid upgrading it
|
||||
if (request.url.startsWith(urlRoot)) {
|
||||
log.debug(`NOT upgrading proxyWebSocketRequest ${request.url}`)
|
||||
return
|
||||
}
|
||||
|
||||
const proxyRecord = proxies.find((p) => request.url.startsWith(p.path))
|
||||
if (proxyRecord) {
|
||||
log.debug(`upgrade proxyWebSocketRequest ${request.url} to ${proxyRecord.host}:${proxyRecord.port}`)
|
||||
request.url = request.url.replace(proxyRecord.path, proxyRecord.baseUrl)
|
||||
proxyRecord.proxy.ws(request, socket, head)
|
||||
}
|
||||
}
|
||||
|
||||
return createProxy
|
||||
}
|
||||
|
||||
exports.create = function (/* config */config, /* config.proxies */proxies, /* emitter */emitter) {
|
||||
const proxyRecords = parseProxyConfig(proxies, config)
|
||||
emitter.on('exit', (done) => {
|
||||
log.debug('Destroying proxy agents')
|
||||
proxyRecords.forEach((proxyRecord) => {
|
||||
proxyRecord.agent.destroy()
|
||||
})
|
||||
done()
|
||||
})
|
||||
return createProxyHandler(proxyRecords, config.urlRoot)
|
||||
}
|
114
my-app/node_modules/karma/lib/middleware/runner.js
generated
vendored
Executable file
114
my-app/node_modules/karma/lib/middleware/runner.js
generated
vendored
Executable file
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* Runner middleware is responsible for communication with `karma run`.
|
||||
*
|
||||
* It basically triggers a test run and streams stdout back.
|
||||
*/
|
||||
|
||||
const _ = require('lodash')
|
||||
const path = require('path')
|
||||
const helper = require('../helper')
|
||||
const log = require('../logger').create()
|
||||
const constant = require('../constants')
|
||||
const json = require('body-parser').json()
|
||||
|
||||
// TODO(vojta): disable when single-run mode
|
||||
function createRunnerMiddleware (emitter, fileList, capturedBrowsers, reporter, executor,
|
||||
/* config.protocol */ protocol, /* config.hostname */ hostname, /* config.port */
|
||||
port, /* config.urlRoot */ urlRoot, config) {
|
||||
helper.saveOriginalArgs(config)
|
||||
return function (request, response, next) {
|
||||
if (request.url !== '/__run__' && request.url !== urlRoot + 'run') {
|
||||
return next()
|
||||
}
|
||||
|
||||
log.debug('Execution (fired by runner)')
|
||||
response.writeHead(200)
|
||||
|
||||
if (!capturedBrowsers.length) {
|
||||
const url = `${protocol}//${hostname}:${port}${urlRoot}`
|
||||
return response.end(`No captured browser, open ${url}\n`)
|
||||
}
|
||||
|
||||
json(request, response, function () {
|
||||
if (!capturedBrowsers.areAllReady([])) {
|
||||
response.write('Waiting for previous execution...\n')
|
||||
}
|
||||
|
||||
const data = request.body
|
||||
|
||||
updateClientArgs(data)
|
||||
handleRun(data)
|
||||
refreshFileList(data).then(() => {
|
||||
executor.schedule()
|
||||
}).catch((error) => {
|
||||
const errorMessage = `Error during refresh file list. ${error.stack || error}`
|
||||
executor.scheduleError(errorMessage)
|
||||
})
|
||||
})
|
||||
|
||||
function updateClientArgs (data) {
|
||||
helper.restoreOriginalArgs(config)
|
||||
if (_.isEmpty(data.args)) {
|
||||
log.debug('Ignoring empty client.args from run command')
|
||||
} else if ((_.isArray(data.args) && _.isArray(config.client.args)) ||
|
||||
(_.isPlainObject(data.args) && _.isPlainObject(config.client.args))) {
|
||||
log.debug('Merging client.args with ', data.args)
|
||||
config.client.args = _.merge(config.client.args, data.args)
|
||||
} else {
|
||||
log.warn('Replacing client.args with ', data.args, ' as their types do not match.')
|
||||
config.client.args = data.args
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshFileList (data) {
|
||||
let fullRefresh = true
|
||||
|
||||
if (helper.isArray(data.changedFiles)) {
|
||||
await Promise.all(data.changedFiles.map(async function (filepath) {
|
||||
await fileList.changeFile(path.resolve(config.basePath, filepath))
|
||||
fullRefresh = false
|
||||
}))
|
||||
}
|
||||
|
||||
if (helper.isArray(data.addedFiles)) {
|
||||
await Promise.all(data.addedFiles.map(async function (filepath) {
|
||||
await fileList.addFile(path.resolve(config.basePath, filepath))
|
||||
fullRefresh = false
|
||||
}))
|
||||
}
|
||||
|
||||
if (helper.isArray(data.removedFiles)) {
|
||||
await Promise.all(data.removedFiles.map(async function (filepath) {
|
||||
await fileList.removeFile(path.resolve(config.basePath, filepath))
|
||||
fullRefresh = false
|
||||
}))
|
||||
}
|
||||
|
||||
if (fullRefresh && data.refresh !== false) {
|
||||
log.debug('Refreshing all the files / patterns')
|
||||
await fileList.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
function handleRun (data) {
|
||||
emitter.once('run_start', function () {
|
||||
const responseWrite = response.write.bind(response)
|
||||
responseWrite.colors = data.colors
|
||||
reporter.addAdapter(responseWrite)
|
||||
|
||||
// clean up, close runner response
|
||||
emitter.once('run_complete', function (_browsers, results) {
|
||||
reporter.removeAdapter(responseWrite)
|
||||
const emptyTestSuite = (results.failed + results.success) === 0 ? 0 : 1
|
||||
response.end(constant.EXIT_CODE + emptyTestSuite + results.exitCode)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createRunnerMiddleware.$inject = ['emitter', 'fileList', 'capturedBrowsers', 'reporter', 'executor',
|
||||
'config.protocol', 'config.hostname', 'config.port', 'config.urlRoot', 'config']
|
||||
|
||||
// PUBLIC API
|
||||
exports.create = createRunnerMiddleware
|
67
my-app/node_modules/karma/lib/middleware/source_files.js
generated
vendored
Executable file
67
my-app/node_modules/karma/lib/middleware/source_files.js
generated
vendored
Executable file
|
@ -0,0 +1,67 @@
|
|||
'use strict'
|
||||
|
||||
const querystring = require('querystring')
|
||||
const common = require('./common')
|
||||
|
||||
const log = require('../logger').create('middleware:source-files')
|
||||
|
||||
function findByPath (files, path) {
|
||||
return Array.from(files).find((file) => file.path === path)
|
||||
}
|
||||
|
||||
function composeUrl (url, basePath, urlRoot) {
|
||||
return url
|
||||
.replace(urlRoot, '/')
|
||||
.replace(/\?.*$/, '')
|
||||
.replace(/^\/absolute/, '')
|
||||
.replace(/^\/base/, basePath)
|
||||
}
|
||||
|
||||
// Source Files middleware is responsible for serving all the source files under the test.
|
||||
function createSourceFilesMiddleware (filesPromise, serveFile, basePath, urlRoot) {
|
||||
return function (request, response, next) {
|
||||
const requestedFilePath = composeUrl(request.url, basePath, urlRoot)
|
||||
// When a path contains HTML-encoded characters (e.g %2F used by Jenkins for branches with /)
|
||||
const requestedFilePathUnescaped = composeUrl(querystring.unescape(request.url), basePath, urlRoot)
|
||||
|
||||
request.pause()
|
||||
|
||||
log.debug(`Requesting ${request.url}`)
|
||||
log.debug(`Fetching ${requestedFilePath}`)
|
||||
|
||||
return filesPromise.then(function (files) {
|
||||
// TODO(vojta): change served to be a map rather then an array
|
||||
const file = findByPath(files.served, requestedFilePath) || findByPath(files.served, requestedFilePathUnescaped)
|
||||
const rangeHeader = request.headers.range
|
||||
|
||||
if (file) {
|
||||
const acceptEncodingHeader = request.headers['accept-encoding']
|
||||
const matchedEncoding = Object.keys(file.encodings).find(
|
||||
(encoding) => new RegExp(`(^|.*, ?)${encoding}(,|$)`).test(acceptEncodingHeader)
|
||||
)
|
||||
const content = file.encodings[matchedEncoding] || file.content
|
||||
|
||||
serveFile(file.contentPath || file.path, rangeHeader, response, function () {
|
||||
if (/\?\w+/.test(request.url)) {
|
||||
common.setHeavyCacheHeaders(response) // files with timestamps - cache one year, rely on timestamps
|
||||
} else {
|
||||
common.setNoCacheHeaders(response) // without timestamps - no cache (debug)
|
||||
}
|
||||
if (matchedEncoding) {
|
||||
response.setHeader('Content-Encoding', matchedEncoding)
|
||||
}
|
||||
}, content, file.doNotCache)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
||||
request.resume()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
createSourceFilesMiddleware.$inject = [
|
||||
'filesPromise', 'serveFile', 'config.basePath', 'config.urlRoot'
|
||||
]
|
||||
|
||||
exports.create = createSourceFilesMiddleware
|
18
my-app/node_modules/karma/lib/middleware/stopper.js
generated
vendored
Executable file
18
my-app/node_modules/karma/lib/middleware/stopper.js
generated
vendored
Executable file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Stopper middleware is responsible for communicating with `karma stop`.
|
||||
*/
|
||||
|
||||
const log = require('../logger').create('middleware:stopper')
|
||||
|
||||
function createStopperMiddleware (urlRoot) {
|
||||
return function (request, response, next) {
|
||||
if (request.url !== urlRoot + 'stop') return next()
|
||||
response.writeHead(200)
|
||||
log.info('Stopping server')
|
||||
response.end('OK')
|
||||
process.kill(process.pid, 'SIGINT')
|
||||
}
|
||||
}
|
||||
|
||||
createStopperMiddleware.$inject = ['config.urlRoot']
|
||||
exports.create = createStopperMiddleware
|
11
my-app/node_modules/karma/lib/middleware/strip_host.js
generated
vendored
Executable file
11
my-app/node_modules/karma/lib/middleware/strip_host.js
generated
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* Strip hostname from request path
|
||||
* This to handle requests that uses (normally over proxies) an absoluteURI as request path
|
||||
*/
|
||||
|
||||
function stripHostFromUrl (url) {
|
||||
return url.replace(/^https?:\/\/[a-z.:\d-]+\//, '/')
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
exports.stripHost = stripHostFromUrl
|
98
my-app/node_modules/karma/lib/plugin.js
generated
vendored
Executable file
98
my-app/node_modules/karma/lib/plugin.js
generated
vendored
Executable file
|
@ -0,0 +1,98 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const path = require('path')
|
||||
const helper = require('./helper')
|
||||
|
||||
const log = require('./logger').create('plugin')
|
||||
|
||||
const IGNORED_PACKAGES = ['karma-cli', 'karma-runner.github.com']
|
||||
|
||||
function resolve (plugins, emitter) {
|
||||
const modules = []
|
||||
|
||||
function requirePlugin (name) {
|
||||
log.debug(`Loading plugin ${name}.`)
|
||||
try {
|
||||
modules.push(require(name))
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND' && e.message.includes(name)) {
|
||||
log.error(`Cannot find plugin "${name}".\n Did you forget to install it?\n npm install ${name} --save-dev`)
|
||||
} else {
|
||||
log.error(`Error during loading "${name}" plugin:\n ${e.message}`)
|
||||
}
|
||||
emitter.emit('load_error', 'plug_in', name)
|
||||
}
|
||||
}
|
||||
|
||||
plugins.forEach(function (plugin) {
|
||||
if (helper.isString(plugin)) {
|
||||
if (!plugin.includes('*')) {
|
||||
requirePlugin(plugin)
|
||||
return
|
||||
}
|
||||
const pluginDirectory = path.normalize(path.join(__dirname, '/../..'))
|
||||
const regexp = new RegExp(`^${plugin.replace(/\*/g, '.*').replace(/\//g, '[/\\\\]')}`)
|
||||
|
||||
log.debug(`Loading ${plugin} from ${pluginDirectory}`)
|
||||
fs.readdirSync(pluginDirectory)
|
||||
.map((e) => {
|
||||
const modulePath = path.join(pluginDirectory, e)
|
||||
if (e[0] === '@') {
|
||||
return fs.readdirSync(modulePath).map((e) => path.join(modulePath, e))
|
||||
}
|
||||
return modulePath
|
||||
})
|
||||
.reduce((a, x) => a.concat(x), [])
|
||||
.map((modulePath) => path.relative(pluginDirectory, modulePath))
|
||||
.filter((moduleName) => !IGNORED_PACKAGES.includes(moduleName) && regexp.test(moduleName))
|
||||
.forEach((pluginName) => requirePlugin(path.join(pluginDirectory, pluginName)))
|
||||
} else if (helper.isObject(plugin)) {
|
||||
log.debug(`Loading inline plugin defining ${Object.keys(plugin).join(', ')}.`)
|
||||
modules.push(plugin)
|
||||
} else {
|
||||
log.error(`Invalid plugin ${plugin}`)
|
||||
emitter.emit('load_error', 'plug_in', plugin)
|
||||
}
|
||||
})
|
||||
|
||||
return modules
|
||||
}
|
||||
|
||||
/**
|
||||
Create a function to handle errors in plugin loading.
|
||||
@param {Object} injector, the dict of dependency injection objects.
|
||||
@return function closed over injector, which reports errors.
|
||||
*/
|
||||
function createInstantiatePlugin (injector) {
|
||||
const emitter = injector.get('emitter')
|
||||
// Cache to avoid report errors multiple times per plugin.
|
||||
const pluginInstances = new Map()
|
||||
return function instantiatePlugin (kind, name) {
|
||||
if (pluginInstances.has(name)) {
|
||||
return pluginInstances.get(name)
|
||||
}
|
||||
|
||||
let p
|
||||
try {
|
||||
p = injector.get(`${kind}:${name}`)
|
||||
if (!p) {
|
||||
log.error(`Failed to instantiate ${kind} ${name}`)
|
||||
emitter.emit('load_error', kind, name)
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message.includes(`No provider for "${kind}:${name}"`)) {
|
||||
log.error(`Cannot load "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
|
||||
} else {
|
||||
log.error(`Cannot load "${name}"!\n ` + e.stack)
|
||||
}
|
||||
emitter.emit('load_error', kind, name)
|
||||
}
|
||||
pluginInstances.set(name, p, `${kind}:${name}`)
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
createInstantiatePlugin.$inject = ['injector']
|
||||
|
||||
module.exports = { resolve, createInstantiatePlugin }
|
111
my-app/node_modules/karma/lib/preprocessor.js
generated
vendored
Executable file
111
my-app/node_modules/karma/lib/preprocessor.js
generated
vendored
Executable file
|
@ -0,0 +1,111 @@
|
|||
'use strict'
|
||||
|
||||
const util = require('util')
|
||||
const fs = require('graceful-fs')
|
||||
// bind need only for mock unit tests
|
||||
const readFile = util.promisify(fs.readFile.bind(fs))
|
||||
const tryToRead = function (path, log) {
|
||||
const maxRetries = 3
|
||||
let promise = readFile(path)
|
||||
for (let retryCount = 1; retryCount <= maxRetries; retryCount++) {
|
||||
promise = promise.catch((err) => {
|
||||
log.warn(err)
|
||||
log.warn('retrying ' + retryCount)
|
||||
return readFile(path)
|
||||
})
|
||||
}
|
||||
return promise.catch((err) => {
|
||||
log.warn(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
const mm = require('minimatch')
|
||||
const { isBinaryFile } = require('isbinaryfile')
|
||||
const _ = require('lodash')
|
||||
const CryptoUtils = require('./utils/crypto-utils')
|
||||
|
||||
const log = require('./logger').create('preprocess')
|
||||
|
||||
function executeProcessor (process, file, content) {
|
||||
let done = null
|
||||
const donePromise = new Promise((resolve, reject) => {
|
||||
done = function (error, content) {
|
||||
// normalize B-C
|
||||
if (arguments.length === 1 && typeof error === 'string') {
|
||||
content = error
|
||||
error = null
|
||||
}
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(content)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return (process(content, file, done) || Promise.resolve()).then((content) => {
|
||||
if (content) {
|
||||
// async process correctly returned content
|
||||
return content
|
||||
}
|
||||
// process called done() (Either old sync api or an async function that did not return content)
|
||||
return donePromise
|
||||
})
|
||||
}
|
||||
|
||||
async function runProcessors (preprocessors, file, content) {
|
||||
try {
|
||||
for (const process of preprocessors) {
|
||||
content = await executeProcessor(process, file, content)
|
||||
}
|
||||
} catch (error) {
|
||||
file.contentPath = null
|
||||
file.content = null
|
||||
throw error
|
||||
}
|
||||
|
||||
file.contentPath = null
|
||||
file.content = content
|
||||
file.sha = CryptoUtils.sha1(content)
|
||||
}
|
||||
|
||||
function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, instantiatePlugin) {
|
||||
_.union.apply(_, Object.values(config)).forEach((name) => instantiatePlugin('preprocessor', name))
|
||||
return async function preprocess (file) {
|
||||
const buffer = await tryToRead(file.originalPath, log)
|
||||
let isBinary = file.isBinary
|
||||
if (isBinary == null) {
|
||||
// Pattern did not specify, probe for it.
|
||||
isBinary = await isBinaryFile(buffer, buffer.length)
|
||||
}
|
||||
|
||||
const preprocessorNames = Object.keys(config).reduce((ppNames, pattern) => {
|
||||
if (mm(file.originalPath, pattern, { dot: true })) {
|
||||
ppNames = _.union(ppNames, config[pattern])
|
||||
}
|
||||
return ppNames
|
||||
}, [])
|
||||
|
||||
// Apply preprocessor priority.
|
||||
const preprocessors = preprocessorNames
|
||||
.map((name) => [name, preprocessorPriority[name] || 0])
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map((duo) => duo[0])
|
||||
.reduce((preProcs, name) => {
|
||||
const p = instantiatePlugin('preprocessor', name)
|
||||
|
||||
if (!isBinary || (p && p.handleBinaryFiles)) {
|
||||
preProcs.push(p)
|
||||
} else {
|
||||
log.warn(`Ignored preprocessing ${file.originalPath} because ${name} has handleBinaryFiles=false.`)
|
||||
}
|
||||
return preProcs
|
||||
}, [])
|
||||
|
||||
await runProcessors(preprocessors, file, isBinary ? buffer : buffer.toString())
|
||||
}
|
||||
}
|
||||
|
||||
createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'instantiatePlugin']
|
||||
exports.createPriorityPreprocessor = createPriorityPreprocessor
|
152
my-app/node_modules/karma/lib/reporter.js
generated
vendored
Executable file
152
my-app/node_modules/karma/lib/reporter.js
generated
vendored
Executable file
|
@ -0,0 +1,152 @@
|
|||
'use strict'
|
||||
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
const resolve = require('url').resolve
|
||||
const SourceMapConsumer = require('source-map').SourceMapConsumer
|
||||
const _ = require('lodash')
|
||||
|
||||
const PathUtils = require('./utils/path-utils')
|
||||
const log = require('./logger').create('reporter')
|
||||
const MultiReporter = require('./reporters/multi')
|
||||
const baseReporterDecoratorFactory = require('./reporters/base').decoratorFactory
|
||||
|
||||
function createErrorFormatter (config, emitter, SourceMapConsumer) {
|
||||
const basePath = config.basePath
|
||||
const urlRoot = config.urlRoot === '/' ? '' : (config.urlRoot || '')
|
||||
let lastServedFiles = []
|
||||
|
||||
emitter.on('file_list_modified', (files) => {
|
||||
lastServedFiles = files.served
|
||||
})
|
||||
|
||||
const URL_REGEXP = new RegExp('(?:https?:\\/\\/' +
|
||||
config.hostname + '(?:\\:' + config.port + ')?' + ')?\\/?' +
|
||||
urlRoot + '\\/?' +
|
||||
'(base/|absolute)' + // prefix, including slash for base/ to create relative paths.
|
||||
'((?:[A-z]\\:)?[^\\?\\s\\:]*)' + // path
|
||||
'(\\?\\w*)?' + // sha
|
||||
'(\\:(\\d+))?' + // line
|
||||
'(\\:(\\d+))?' + // column
|
||||
'', 'g')
|
||||
|
||||
const cache = new WeakMap()
|
||||
|
||||
function getSourceMapConsumer (sourceMap) {
|
||||
if (!cache.has(sourceMap)) {
|
||||
cache.set(sourceMap, new SourceMapConsumer(sourceMap))
|
||||
}
|
||||
return cache.get(sourceMap)
|
||||
}
|
||||
|
||||
return function (input, indentation) {
|
||||
indentation = _.isString(indentation) ? indentation : ''
|
||||
if (_.isError(input)) {
|
||||
input = input.message
|
||||
} else if (_.isEmpty(input)) {
|
||||
input = ''
|
||||
} else if (!_.isString(input)) {
|
||||
input = JSON.stringify(input, null, indentation)
|
||||
}
|
||||
|
||||
let msg = input.replace(URL_REGEXP, function (stackTracePath, prefix, path, __, ___, line, ____, column) {
|
||||
const normalizedPath = prefix === 'base/' ? `${basePath}/${path}` : path
|
||||
const file = lastServedFiles.find((file) => file.path === normalizedPath)
|
||||
|
||||
if (file && file.sourceMap && line) {
|
||||
line = +line
|
||||
column = +column
|
||||
|
||||
// When no column is given and we default to 0, it doesn't make sense to only search for smaller
|
||||
// or equal columns in the sourcemap, let's search for equal or greater columns.
|
||||
const bias = column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND
|
||||
|
||||
try {
|
||||
const zeroBasedColumn = Math.max(0, (column || 1) - 1)
|
||||
const original = getSourceMapConsumer(file.sourceMap).originalPositionFor({ line, column: zeroBasedColumn, bias })
|
||||
|
||||
// If there is no original position/source for the current stack trace path, then
|
||||
// we return early with the formatted generated position. This handles the case of
|
||||
// generated code which does not map to anything, see Case 1 of the source-map spec.
|
||||
// https://sourcemaps.info/spec.html.
|
||||
if (original.source === null) {
|
||||
return PathUtils.formatPathMapping(path, line, column)
|
||||
}
|
||||
|
||||
// Source maps often only have a local file name, resolve to turn into a full path if
|
||||
// the path is not absolute yet.
|
||||
const oneBasedOriginalColumn = original.column == null ? original.column : original.column + 1
|
||||
return `${PathUtils.formatPathMapping(resolve(path, original.source), original.line, oneBasedOriginalColumn)} <- ${PathUtils.formatPathMapping(path, line, column)}`
|
||||
} catch (e) {
|
||||
log.warn(`An unexpected error occurred while resolving the original position for: ${stackTracePath}`)
|
||||
log.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
return PathUtils.formatPathMapping(path, line, column) || prefix
|
||||
})
|
||||
|
||||
if (indentation) {
|
||||
msg = indentation + msg.replace(/\n/g, '\n' + indentation)
|
||||
}
|
||||
|
||||
return config.formatError ? config.formatError(msg) : msg + '\n'
|
||||
}
|
||||
}
|
||||
|
||||
function createReporters (names, config, emitter, injector) {
|
||||
const errorFormatter = createErrorFormatter(config, emitter, SourceMapConsumer)
|
||||
const reporters = []
|
||||
|
||||
names.forEach((name) => {
|
||||
if (['dots', 'progress'].includes(name)) {
|
||||
[
|
||||
require(`./reporters/${name}`),
|
||||
require(`./reporters/${name}_color`)
|
||||
].forEach((Reporter) => {
|
||||
reporters.push(new Reporter(errorFormatter, config.reportSlowerThan, config.colors, config.browserConsoleLogOptions))
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const locals = {
|
||||
baseReporterDecorator: ['factory', baseReporterDecoratorFactory],
|
||||
formatError: ['value', errorFormatter]
|
||||
}
|
||||
|
||||
try {
|
||||
log.debug(`Trying to load reporter: ${name}`)
|
||||
reporters.push(injector.createChild([locals], ['reporter:' + name]).get('reporter:' + name))
|
||||
} catch (e) {
|
||||
if (e.message.includes(`No provider for "reporter:${name}"`)) {
|
||||
log.error(`Can not load reporter "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
|
||||
} else {
|
||||
log.error(`Can not load "${name}"!\n ${e.stack}`)
|
||||
}
|
||||
emitter.emit('load_error', 'reporter', name)
|
||||
return
|
||||
}
|
||||
|
||||
const colorName = name + '_color'
|
||||
if (!names.includes(colorName)) {
|
||||
try {
|
||||
log.debug(`Trying to load color-version of reporter: ${name} (${colorName})`)
|
||||
reporters.push(injector.createChild([locals], ['reporter:' + colorName]).get('reporter:' + name))
|
||||
} catch (e) {
|
||||
log.debug('Couldn\'t load color-version.')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
reporters.forEach((reporter) => emitter.bind(reporter))
|
||||
|
||||
return new MultiReporter(reporters)
|
||||
}
|
||||
|
||||
createReporters.$inject = [
|
||||
'config.reporters',
|
||||
'config',
|
||||
'emitter',
|
||||
'injector'
|
||||
]
|
||||
|
||||
exports.createReporters = createReporters
|
162
my-app/node_modules/karma/lib/reporters/base.js
generated
vendored
Executable file
162
my-app/node_modules/karma/lib/reporters/base.js
generated
vendored
Executable file
|
@ -0,0 +1,162 @@
|
|||
'use strict'
|
||||
|
||||
const util = require('util')
|
||||
|
||||
const constants = require('../constants')
|
||||
const helper = require('../helper')
|
||||
|
||||
const BaseReporter = function (formatError, reportSlow, useColors, browserConsoleLogOptions, adapter) {
|
||||
this.adapters = [adapter || process.stdout.write.bind(process.stdout)]
|
||||
|
||||
this.USE_COLORS = false
|
||||
this.EXCLUSIVELY_USE_COLORS = undefined
|
||||
this.LOG_SINGLE_BROWSER = '%s: %s\n'
|
||||
this.LOG_MULTI_BROWSER = '%s %s: %s\n'
|
||||
|
||||
this.SPEC_FAILURE = '%s %s FAILED' + '\n'
|
||||
this.SPEC_SLOW = '%s SLOW %s: %s\n'
|
||||
this.ERROR = '%s ERROR\n'
|
||||
|
||||
this.FINISHED_ERROR = ' ERROR'
|
||||
this.FINISHED_SUCCESS = ' SUCCESS'
|
||||
this.FINISHED_DISCONNECTED = ' DISCONNECTED'
|
||||
|
||||
this.X_FAILED = ' (%d FAILED)'
|
||||
|
||||
this.TOTAL_SUCCESS = 'TOTAL: %d SUCCESS\n'
|
||||
this.TOTAL_FAILED = 'TOTAL: %d FAILED, %d SUCCESS\n'
|
||||
|
||||
this.onRunStart = () => {
|
||||
this._browsers = []
|
||||
}
|
||||
|
||||
this.onBrowserStart = (browser) => {
|
||||
this._browsers.push(browser)
|
||||
}
|
||||
|
||||
this.renderBrowser = (browser) => {
|
||||
const results = browser.lastResult
|
||||
const totalExecuted = results.success + results.failed
|
||||
let msg = `${browser}: Executed ${totalExecuted} of ${results.total}`
|
||||
|
||||
if (results.failed) {
|
||||
msg += util.format(this.X_FAILED, results.failed)
|
||||
}
|
||||
|
||||
if (results.skipped) {
|
||||
msg += ` (skipped ${results.skipped})`
|
||||
}
|
||||
|
||||
if (browser.isConnected) {
|
||||
if (results.disconnected) {
|
||||
msg += this.FINISHED_DISCONNECTED
|
||||
} else if (results.error) {
|
||||
msg += this.FINISHED_ERROR
|
||||
} else if (!results.failed) {
|
||||
msg += this.FINISHED_SUCCESS
|
||||
}
|
||||
|
||||
msg += ` (${helper.formatTimeInterval(results.totalTime)} / ${helper.formatTimeInterval(results.netTime)})`
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
this.write = function () {
|
||||
const msg = util.format.apply(null, Array.prototype.slice.call(arguments))
|
||||
this.adapters.forEach((adapter) => {
|
||||
if (!helper.isDefined(adapter.colors)) {
|
||||
adapter.colors = useColors
|
||||
}
|
||||
if (!helper.isDefined(this.EXCLUSIVELY_USE_COLORS) || adapter.colors === this.EXCLUSIVELY_USE_COLORS) {
|
||||
return adapter(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.writeCommonMsg = function () {
|
||||
this.write.apply(this, arguments)
|
||||
}
|
||||
|
||||
this.onBrowserError = (browser, error) => {
|
||||
this.writeCommonMsg(util.format(this.ERROR, browser) + formatError(error, ' '))
|
||||
}
|
||||
|
||||
this.onBrowserLog = (browser, log, type) => {
|
||||
if (!browserConsoleLogOptions || !browserConsoleLogOptions.terminal) return
|
||||
type = type.toUpperCase()
|
||||
if (browserConsoleLogOptions.level) {
|
||||
const logPriority = constants.LOG_PRIORITIES.indexOf(browserConsoleLogOptions.level.toUpperCase())
|
||||
if (constants.LOG_PRIORITIES.indexOf(type) > logPriority) return
|
||||
}
|
||||
if (!helper.isString(log)) {
|
||||
// TODO(vojta): change util to new syntax (config object)
|
||||
log = util.inspect(log, false, undefined, this.USE_COLORS)
|
||||
}
|
||||
if (this._browsers && this._browsers.length === 1) {
|
||||
this.writeCommonMsg(util.format(this.LOG_SINGLE_BROWSER, type, log))
|
||||
} else {
|
||||
this.writeCommonMsg(util.format(this.LOG_MULTI_BROWSER, browser, type, log))
|
||||
}
|
||||
}
|
||||
|
||||
this.onSpecComplete = (browser, result) => {
|
||||
if (result.skipped) {
|
||||
this.specSkipped(browser, result)
|
||||
} else if (result.success) {
|
||||
this.specSuccess(browser, result)
|
||||
} else {
|
||||
this.specFailure(browser, result)
|
||||
}
|
||||
|
||||
if (reportSlow && result.time > reportSlow) {
|
||||
const specName = result.suite.join(' ') + ' ' + result.description
|
||||
const time = helper.formatTimeInterval(result.time)
|
||||
|
||||
this.writeCommonMsg(util.format(this.SPEC_SLOW, browser, time, specName))
|
||||
}
|
||||
}
|
||||
|
||||
this.specSuccess = () => {
|
||||
}
|
||||
|
||||
this.specSkipped = () => {
|
||||
}
|
||||
|
||||
this.specFailure = (browser, result) => {
|
||||
const specName = result.suite.join(' ') + ' ' + result.description
|
||||
let msg = util.format(this.SPEC_FAILURE, browser, specName)
|
||||
|
||||
result.log.forEach((log) => {
|
||||
msg += formatError(log, '\t')
|
||||
})
|
||||
|
||||
this.writeCommonMsg(msg)
|
||||
}
|
||||
|
||||
this.onRunComplete = (browsers, results) => {
|
||||
if (browsers.length >= 1 && !results.error && !results.disconnected) {
|
||||
if (!results.failed) {
|
||||
this.write(this.TOTAL_SUCCESS, results.success)
|
||||
} else {
|
||||
this.write(this.TOTAL_FAILED, results.failed, results.success)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseReporter.decoratorFactory = function (formatError, reportSlow, useColors, browserConsoleLogOptions) {
|
||||
return function (self) {
|
||||
BaseReporter.call(self, formatError, reportSlow, useColors, browserConsoleLogOptions)
|
||||
}
|
||||
}
|
||||
|
||||
BaseReporter.decoratorFactory.$inject = [
|
||||
'formatError',
|
||||
'config.reportSlowerThan',
|
||||
'config.colors',
|
||||
'config.browserConsoleLogOptions'
|
||||
]
|
||||
|
||||
// PUBLISH
|
||||
module.exports = BaseReporter
|
24
my-app/node_modules/karma/lib/reporters/base_color.js
generated
vendored
Executable file
24
my-app/node_modules/karma/lib/reporters/base_color.js
generated
vendored
Executable file
|
@ -0,0 +1,24 @@
|
|||
const { red, yellow, green, cyan } = require('@colors/colors/safe')
|
||||
|
||||
function BaseColorReporter () {
|
||||
this.USE_COLORS = true
|
||||
|
||||
this.LOG_SINGLE_BROWSER = '%s: ' + cyan('%s') + '\n'
|
||||
this.LOG_MULTI_BROWSER = '%s %s: ' + cyan('%s') + '\n'
|
||||
|
||||
this.SPEC_FAILURE = red('%s %s FAILED') + '\n'
|
||||
this.SPEC_SLOW = yellow('%s SLOW %s: %s') + '\n'
|
||||
this.ERROR = red('%s ERROR') + '\n'
|
||||
|
||||
this.FINISHED_ERROR = red(' ERROR')
|
||||
this.FINISHED_SUCCESS = green(' SUCCESS')
|
||||
this.FINISHED_DISCONNECTED = red(' DISCONNECTED')
|
||||
|
||||
this.X_FAILED = red(' (%d FAILED)')
|
||||
|
||||
this.TOTAL_SUCCESS = green('TOTAL: %d SUCCESS') + '\n'
|
||||
this.TOTAL_FAILED = red('TOTAL: %d FAILED, %d SUCCESS') + '\n'
|
||||
}
|
||||
|
||||
// PUBLISH
|
||||
module.exports = BaseColorReporter
|
47
my-app/node_modules/karma/lib/reporters/dots.js
generated
vendored
Executable file
47
my-app/node_modules/karma/lib/reporters/dots.js
generated
vendored
Executable file
|
@ -0,0 +1,47 @@
|
|||
const BaseReporter = require('./base')
|
||||
|
||||
function DotsReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
|
||||
BaseReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
|
||||
|
||||
const DOTS_WRAP = 80
|
||||
this.EXCLUSIVELY_USE_COLORS = false
|
||||
this.onRunStart = function () {
|
||||
this._browsers = []
|
||||
this._dotsCount = 0
|
||||
}
|
||||
|
||||
this.onBrowserStart = function (browser) {
|
||||
this._browsers.push(browser)
|
||||
}
|
||||
|
||||
this.writeCommonMsg = function (msg) {
|
||||
if (this._dotsCount) {
|
||||
this._dotsCount = 0
|
||||
msg = '\n' + msg
|
||||
}
|
||||
|
||||
this.write(msg)
|
||||
}
|
||||
|
||||
this.specSuccess = function () {
|
||||
this._dotsCount = (this._dotsCount + 1) % DOTS_WRAP
|
||||
this.write(this._dotsCount ? '.' : '.\n')
|
||||
}
|
||||
|
||||
this.onBrowserComplete = function (browser) {
|
||||
this.writeCommonMsg(this.renderBrowser(browser) + '\n')
|
||||
}
|
||||
|
||||
this.onRunComplete = function (browsers, results) {
|
||||
if (browsers.length > 1 && !results.disconnected && !results.error) {
|
||||
if (!results.failed) {
|
||||
this.write(this.TOTAL_SUCCESS, results.success)
|
||||
} else {
|
||||
this.write(this.TOTAL_FAILED, results.failed, results.success)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLISH
|
||||
module.exports = DotsReporter
|
11
my-app/node_modules/karma/lib/reporters/dots_color.js
generated
vendored
Executable file
11
my-app/node_modules/karma/lib/reporters/dots_color.js
generated
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
const DotsReporter = require('./dots')
|
||||
const BaseColorReporter = require('./base_color')
|
||||
|
||||
function DotsColorReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
|
||||
DotsReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
|
||||
BaseColorReporter.call(this)
|
||||
this.EXCLUSIVELY_USE_COLORS = true
|
||||
}
|
||||
|
||||
// PUBLISH
|
||||
module.exports = DotsColorReporter
|
19
my-app/node_modules/karma/lib/reporters/multi.js
generated
vendored
Executable file
19
my-app/node_modules/karma/lib/reporters/multi.js
generated
vendored
Executable file
|
@ -0,0 +1,19 @@
|
|||
'use strict'
|
||||
|
||||
const helper = require('../helper')
|
||||
|
||||
class MultiReporter {
|
||||
constructor (reporters) {
|
||||
this._reporters = reporters
|
||||
}
|
||||
|
||||
addAdapter (adapter) {
|
||||
this._reporters.forEach((reporter) => reporter.adapters.push(adapter))
|
||||
}
|
||||
|
||||
removeAdapter (adapter) {
|
||||
this._reporters.forEach((reporter) => helper.arrayRemove(reporter.adapters, adapter))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MultiReporter
|
63
my-app/node_modules/karma/lib/reporters/progress.js
generated
vendored
Executable file
63
my-app/node_modules/karma/lib/reporters/progress.js
generated
vendored
Executable file
|
@ -0,0 +1,63 @@
|
|||
const BaseReporter = require('./base')
|
||||
|
||||
function ProgressReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
|
||||
BaseReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
|
||||
|
||||
this.EXCLUSIVELY_USE_COLORS = false
|
||||
this._browsers = []
|
||||
|
||||
this.writeCommonMsg = function (msg) {
|
||||
this.write(this._remove() + msg + this._render())
|
||||
}
|
||||
|
||||
this.specSuccess = function () {
|
||||
this.write(this._refresh())
|
||||
}
|
||||
|
||||
this.onBrowserComplete = function () {
|
||||
this.write(this._refresh())
|
||||
}
|
||||
|
||||
this.onRunStart = function () {
|
||||
this._browsers = []
|
||||
this._isRendered = false
|
||||
}
|
||||
|
||||
this.onBrowserStart = function (browser) {
|
||||
this._browsers.push(browser)
|
||||
|
||||
if (this._isRendered) {
|
||||
this.write('\n')
|
||||
}
|
||||
|
||||
this.write(this._refresh())
|
||||
}
|
||||
|
||||
this._remove = function () {
|
||||
if (!this._isRendered) {
|
||||
return ''
|
||||
}
|
||||
|
||||
let cmd = ''
|
||||
this._browsers.forEach(function () {
|
||||
cmd += '\x1B[1A' + '\x1B[2K'
|
||||
})
|
||||
|
||||
this._isRendered = false
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
this._render = function () {
|
||||
this._isRendered = true
|
||||
|
||||
return this._browsers.map(this.renderBrowser).join('\n') + '\n'
|
||||
}
|
||||
|
||||
this._refresh = function () {
|
||||
return this._remove() + this._render()
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLISH
|
||||
module.exports = ProgressReporter
|
11
my-app/node_modules/karma/lib/reporters/progress_color.js
generated
vendored
Executable file
11
my-app/node_modules/karma/lib/reporters/progress_color.js
generated
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
const ProgressReporter = require('./progress')
|
||||
const BaseColorReporter = require('./base_color')
|
||||
|
||||
function ProgressColorReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
|
||||
ProgressReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
|
||||
BaseColorReporter.call(this)
|
||||
this.EXCLUSIVELY_USE_COLORS = true
|
||||
}
|
||||
|
||||
// PUBLISH
|
||||
module.exports = ProgressColorReporter
|
113
my-app/node_modules/karma/lib/runner.js
generated
vendored
Executable file
113
my-app/node_modules/karma/lib/runner.js
generated
vendored
Executable file
|
@ -0,0 +1,113 @@
|
|||
'use strict'
|
||||
|
||||
const http = require('http')
|
||||
|
||||
const constant = require('./constants')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const helper = require('./helper')
|
||||
const cfg = require('./config')
|
||||
const logger = require('./logger')
|
||||
const { lookup } = require('./utils/dns-utils')
|
||||
const log = logger.create('runner')
|
||||
|
||||
function parseExitCode (buffer, defaultExitCode, failOnEmptyTestSuite) {
|
||||
const tailPos = buffer.length - Buffer.byteLength(constant.EXIT_CODE) - 2
|
||||
|
||||
if (tailPos < 0) {
|
||||
return { exitCode: defaultExitCode, buffer }
|
||||
}
|
||||
|
||||
const tail = buffer.slice(tailPos)
|
||||
const tailStr = tail.toString()
|
||||
if (tailStr.slice(0, -2) === constant.EXIT_CODE) {
|
||||
const emptyInt = parseInt(tailStr.slice(-2, -1), 10)
|
||||
let exitCode = parseInt(tailStr.slice(-1), 10)
|
||||
if (failOnEmptyTestSuite === false && emptyInt === 0) {
|
||||
log.warn('Test suite was empty.')
|
||||
exitCode = 0
|
||||
}
|
||||
return { exitCode, buffer: buffer.slice(0, tailPos) }
|
||||
}
|
||||
|
||||
return { exitCode: defaultExitCode, buffer }
|
||||
}
|
||||
|
||||
// TODO(vojta): read config file (port, host, urlRoot)
|
||||
function run (cliOptionsOrConfig, done) {
|
||||
cliOptionsOrConfig = cliOptionsOrConfig || {}
|
||||
done = helper.isFunction(done) ? done : process.exit
|
||||
|
||||
let config
|
||||
if (cliOptionsOrConfig instanceof cfg.Config) {
|
||||
config = cliOptionsOrConfig
|
||||
} else {
|
||||
logger.setupFromConfig({
|
||||
colors: cliOptionsOrConfig.colors,
|
||||
logLevel: cliOptionsOrConfig.logLevel
|
||||
})
|
||||
const deprecatedCliOptionsMessage =
|
||||
'Passing raw CLI options to `runner(config, done)` is deprecated. Use ' +
|
||||
'`parseConfig(configFilePath, cliOptions, {promiseConfig: true, throwErrors: true})` ' +
|
||||
'to prepare a processed `Config` instance and pass that as the ' +
|
||||
'`config` argument instead.'
|
||||
log.warn(deprecatedCliOptionsMessage)
|
||||
try {
|
||||
config = cfg.parseConfig(
|
||||
cliOptionsOrConfig.configFile,
|
||||
cliOptionsOrConfig,
|
||||
{
|
||||
promiseConfig: false,
|
||||
throwErrors: true
|
||||
}
|
||||
)
|
||||
} catch (parseConfigError) {
|
||||
// TODO: change how `done` falls back to exit in next major version
|
||||
// SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378
|
||||
done(1)
|
||||
}
|
||||
}
|
||||
let exitCode = 1
|
||||
const emitter = new EventEmitter()
|
||||
const options = {
|
||||
hostname: config.hostname,
|
||||
path: config.urlRoot + 'run',
|
||||
port: config.port,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
lookup
|
||||
}
|
||||
|
||||
const request = http.request(options, function (response) {
|
||||
response.on('data', function (buffer) {
|
||||
const parsedResult = parseExitCode(buffer, exitCode, config.failOnEmptyTestSuite)
|
||||
exitCode = parsedResult.exitCode
|
||||
emitter.emit('progress', parsedResult.buffer)
|
||||
})
|
||||
|
||||
response.on('end', () => done(exitCode))
|
||||
})
|
||||
|
||||
request.on('error', function (e) {
|
||||
if (e.code === 'ECONNREFUSED') {
|
||||
log.error('There is no server listening on port %d', options.port)
|
||||
done(1, e.code)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
|
||||
request.end(JSON.stringify({
|
||||
args: config.clientArgs,
|
||||
removedFiles: config.removedFiles,
|
||||
changedFiles: config.changedFiles,
|
||||
addedFiles: config.addedFiles,
|
||||
refresh: config.refresh,
|
||||
colors: config.colors
|
||||
}))
|
||||
|
||||
return emitter
|
||||
}
|
||||
|
||||
exports.run = run
|
487
my-app/node_modules/karma/lib/server.js
generated
vendored
Executable file
487
my-app/node_modules/karma/lib/server.js
generated
vendored
Executable file
|
@ -0,0 +1,487 @@
|
|||
'use strict'
|
||||
|
||||
const SocketIO = require('socket.io')
|
||||
const di = require('di')
|
||||
const util = require('util')
|
||||
const spawn = require('child_process').spawn
|
||||
const tmp = require('tmp')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const NetUtils = require('./utils/net-utils')
|
||||
const root = global || window || this
|
||||
|
||||
const cfg = require('./config')
|
||||
const logger = require('./logger')
|
||||
const constant = require('./constants')
|
||||
const watcher = require('./watcher')
|
||||
const plugin = require('./plugin')
|
||||
|
||||
const createServeFile = require('./web-server').createServeFile
|
||||
const createServeStaticFile = require('./web-server').createServeStaticFile
|
||||
const createFilesPromise = require('./web-server').createFilesPromise
|
||||
const createWebServer = require('./web-server').createWebServer
|
||||
const preprocessor = require('./preprocessor')
|
||||
const Launcher = require('./launcher').Launcher
|
||||
const FileList = require('./file-list')
|
||||
const reporter = require('./reporter')
|
||||
const helper = require('./helper')
|
||||
const events = require('./events')
|
||||
const KarmaEventEmitter = events.EventEmitter
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const Executor = require('./executor')
|
||||
const Browser = require('./browser')
|
||||
const BrowserCollection = require('./browser_collection')
|
||||
const EmitterWrapper = require('./emitter_wrapper')
|
||||
const processWrapper = new EmitterWrapper(process)
|
||||
|
||||
function createSocketIoServer (webServer, executor, config) {
|
||||
const server = new SocketIO.Server(webServer, {
|
||||
// avoid destroying http upgrades from socket.io to get proxied websockets working
|
||||
destroyUpgrade: false,
|
||||
path: config.urlRoot + 'socket.io/',
|
||||
transports: config.transports,
|
||||
forceJSONP: config.forceJSONP,
|
||||
// Default is 5000 in socket.io v2.x and v3.x.
|
||||
pingTimeout: config.pingTimeout || 5000,
|
||||
// Default in v2 is 1e8 and coverage results can fail at 1e6
|
||||
maxHttpBufferSize: 1e8
|
||||
})
|
||||
|
||||
// hack to overcome circular dependency
|
||||
executor.socketIoSockets = server.sockets
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
class Server extends KarmaEventEmitter {
|
||||
constructor (cliOptionsOrConfig, done) {
|
||||
super()
|
||||
cliOptionsOrConfig = cliOptionsOrConfig || {}
|
||||
this.log = logger.create('karma-server')
|
||||
done = helper.isFunction(done) ? done : process.exit
|
||||
this.loadErrors = []
|
||||
|
||||
let config
|
||||
if (cliOptionsOrConfig instanceof cfg.Config) {
|
||||
config = cliOptionsOrConfig
|
||||
} else {
|
||||
logger.setupFromConfig({
|
||||
colors: cliOptionsOrConfig.colors,
|
||||
logLevel: cliOptionsOrConfig.logLevel
|
||||
})
|
||||
const deprecatedCliOptionsMessage =
|
||||
'Passing raw CLI options to `new Server(config, done)` is ' +
|
||||
'deprecated. Use ' +
|
||||
'`parseConfig(configFilePath, cliOptions, {promiseConfig: true, throwErrors: true})` ' +
|
||||
'to prepare a processed `Config` instance and pass that as the ' +
|
||||
'`config` argument instead.'
|
||||
this.log.warn(deprecatedCliOptionsMessage)
|
||||
try {
|
||||
config = cfg.parseConfig(
|
||||
cliOptionsOrConfig.configFile,
|
||||
cliOptionsOrConfig,
|
||||
{
|
||||
promiseConfig: false,
|
||||
throwErrors: true
|
||||
}
|
||||
)
|
||||
} catch (parseConfigError) {
|
||||
// TODO: change how `done` falls back to exit in next major version
|
||||
// SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378
|
||||
done(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.log.debug('Final config', util.inspect(config, false, /** depth **/ null))
|
||||
|
||||
if (!config.autoWatch && !config.singleRun) {
|
||||
this.log.warn('`autowatch` and `singleRun` are both `false`. In order to execute tests use `karma run`.')
|
||||
}
|
||||
|
||||
let modules = [{
|
||||
helper: ['value', helper],
|
||||
logger: ['value', logger],
|
||||
done: ['value', done || process.exit],
|
||||
emitter: ['value', this],
|
||||
server: ['value', this],
|
||||
watcher: ['value', watcher],
|
||||
launcher: ['factory', Launcher.factory],
|
||||
config: ['value', config],
|
||||
instantiatePlugin: ['factory', plugin.createInstantiatePlugin],
|
||||
preprocess: ['factory', preprocessor.createPriorityPreprocessor],
|
||||
fileList: ['factory', FileList.factory],
|
||||
webServer: ['factory', createWebServer],
|
||||
serveFile: ['factory', createServeFile],
|
||||
serveStaticFile: ['factory', createServeStaticFile],
|
||||
filesPromise: ['factory', createFilesPromise],
|
||||
socketServer: ['factory', createSocketIoServer],
|
||||
executor: ['factory', Executor.factory],
|
||||
// TODO: Deprecated. Remove in the next major
|
||||
customFileHandlers: ['value', []],
|
||||
reporter: ['factory', reporter.createReporters],
|
||||
capturedBrowsers: ['factory', BrowserCollection.factory],
|
||||
args: ['value', {}],
|
||||
timer: ['value', {
|
||||
setTimeout () {
|
||||
return setTimeout.apply(root, arguments)
|
||||
},
|
||||
clearTimeout
|
||||
}]
|
||||
}]
|
||||
|
||||
this.on('load_error', (type, name) => {
|
||||
this.log.debug(`Registered a load error of type ${type} with name ${name}`)
|
||||
this.loadErrors.push([type, name])
|
||||
})
|
||||
|
||||
modules = modules.concat(plugin.resolve(config.plugins, this))
|
||||
this._injector = new di.Injector(modules)
|
||||
}
|
||||
|
||||
async start () {
|
||||
const config = this.get('config')
|
||||
try {
|
||||
this._boundServer = await NetUtils.bindAvailablePort(config.port, config.listenAddress)
|
||||
this._boundServer.on('connection', (socket) => {
|
||||
// Attach an error handler to avoid UncaughtException errors.
|
||||
socket.on('error', (err) => {
|
||||
// Errors on this socket are retried, ignore them
|
||||
this.log.debug('Ignoring error on webserver connection: ' + err)
|
||||
})
|
||||
})
|
||||
config.port = this._boundServer.address().port
|
||||
await this._injector.invoke(this._start, this)
|
||||
} catch (err) {
|
||||
this.log.error(`Server start failed on port ${config.port}: ${err}`)
|
||||
this._close(1)
|
||||
}
|
||||
}
|
||||
|
||||
get (token) {
|
||||
return this._injector.get(token)
|
||||
}
|
||||
|
||||
refreshFiles () {
|
||||
return this._fileList ? this._fileList.refresh() : Promise.resolve()
|
||||
}
|
||||
|
||||
refreshFile (path) {
|
||||
return this._fileList ? this._fileList.changeFile(path) : Promise.resolve()
|
||||
}
|
||||
|
||||
emitExitAsync (code) {
|
||||
const name = 'exit'
|
||||
let pending = this.listeners(name).length
|
||||
const deferred = helper.defer()
|
||||
|
||||
function resolve () {
|
||||
deferred.resolve(code)
|
||||
}
|
||||
|
||||
try {
|
||||
this.emit(name, (newCode) => {
|
||||
if (newCode && typeof newCode === 'number') {
|
||||
// Only update code if it is given and not zero
|
||||
code = newCode
|
||||
}
|
||||
if (!--pending) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
if (!pending) {
|
||||
resolve()
|
||||
}
|
||||
} catch (err) {
|
||||
deferred.reject(err)
|
||||
}
|
||||
return deferred.promise
|
||||
}
|
||||
|
||||
async _start (config, launcher, preprocess, fileList, capturedBrowsers, executor, done) {
|
||||
if (config.detached) {
|
||||
this._detach(config, done)
|
||||
return
|
||||
}
|
||||
|
||||
this._fileList = fileList
|
||||
|
||||
await Promise.all(
|
||||
config.frameworks.map((framework) => this._injector.get('framework:' + framework))
|
||||
)
|
||||
|
||||
const webServer = this._injector.get('webServer')
|
||||
const socketServer = this._injector.get('socketServer')
|
||||
|
||||
const singleRunDoneBrowsers = Object.create(null)
|
||||
const singleRunBrowsers = new BrowserCollection(new EventEmitter())
|
||||
let singleRunBrowserNotCaptured = false
|
||||
|
||||
webServer.on('error', (err) => {
|
||||
this.log.error(`Webserver fail ${err}`)
|
||||
this._close(1)
|
||||
})
|
||||
|
||||
const afterPreprocess = () => {
|
||||
if (config.autoWatch) {
|
||||
const watcher = this.get('watcher')
|
||||
this._injector.invoke(watcher)
|
||||
}
|
||||
|
||||
webServer.listen(this._boundServer, () => {
|
||||
this.log.info(`Karma v${constant.VERSION} server started at ${config.protocol}//${config.hostname}:${config.port}${config.urlRoot}`)
|
||||
|
||||
this.emit('listening', config.port)
|
||||
if (config.browsers && config.browsers.length) {
|
||||
this._injector.invoke(launcher.launch, launcher).forEach((browserLauncher) => {
|
||||
singleRunDoneBrowsers[browserLauncher.id] = false
|
||||
})
|
||||
}
|
||||
if (this.loadErrors.length > 0) {
|
||||
this.log.error(new Error(`Found ${this.loadErrors.length} load error${this.loadErrors.length === 1 ? '' : 's'}`))
|
||||
this._close(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fileList.refresh().then(afterPreprocess, (err) => {
|
||||
this.log.error('Error during file loading or preprocessing\n' + err.stack || err)
|
||||
afterPreprocess()
|
||||
})
|
||||
|
||||
this.on('browsers_change', () => socketServer.sockets.emit('info', capturedBrowsers.serialize()))
|
||||
|
||||
this.on('browser_register', (browser) => {
|
||||
launcher.markCaptured(browser.id)
|
||||
|
||||
if (launcher.areAllCaptured()) {
|
||||
this.emit('browsers_ready')
|
||||
|
||||
if (config.autoWatch) {
|
||||
executor.schedule()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (config.browserConsoleLogOptions && config.browserConsoleLogOptions.path) {
|
||||
const configLevel = config.browserConsoleLogOptions.level || 'debug'
|
||||
const configFormat = config.browserConsoleLogOptions.format || '%b %T: %m'
|
||||
const configPath = config.browserConsoleLogOptions.path
|
||||
const configPathDir = path.dirname(configPath)
|
||||
if (!fs.existsSync(configPathDir)) fs.mkdirSync(configPathDir, { recursive: true })
|
||||
this.log.info(`Writing browser console to file: ${configPath}`)
|
||||
const browserLogFile = fs.openSync(configPath, 'w+')
|
||||
const levels = ['log', 'error', 'warn', 'info', 'debug']
|
||||
this.on('browser_log', function (browser, message, level) {
|
||||
if (levels.indexOf(level.toLowerCase()) > levels.indexOf(configLevel)) {
|
||||
return
|
||||
}
|
||||
if (!helper.isString(message)) {
|
||||
message = util.inspect(message, { showHidden: false, colors: false })
|
||||
}
|
||||
const logMap = { '%m': message, '%t': level.toLowerCase(), '%T': level.toUpperCase(), '%b': browser }
|
||||
const logString = configFormat.replace(/%[mtTb]/g, (m) => logMap[m])
|
||||
this.log.debug(`Writing browser console line: ${logString}`)
|
||||
fs.writeSync(browserLogFile, logString + '\n')
|
||||
})
|
||||
}
|
||||
|
||||
socketServer.sockets.on('connection', (socket) => {
|
||||
this.log.debug(`A browser has connected on socket ${socket.id}`)
|
||||
|
||||
const replySocketEvents = events.bufferEvents(socket, ['start', 'info', 'karma_error', 'result', 'complete'])
|
||||
|
||||
socket.on('error', (err) => {
|
||||
this.log.debug('karma server socket error: ' + err)
|
||||
})
|
||||
|
||||
socket.on('register', (info) => {
|
||||
const knownBrowser = info.id ? (capturedBrowsers.getById(info.id) || singleRunBrowsers.getById(info.id)) : null
|
||||
|
||||
if (knownBrowser) {
|
||||
knownBrowser.reconnect(socket, info.isSocketReconnect)
|
||||
} else {
|
||||
const newBrowser = this._injector.createChild([{
|
||||
id: ['value', info.id || null],
|
||||
fullName: ['value', (helper.isDefined(info.displayName) ? info.displayName : info.name)],
|
||||
socket: ['value', socket]
|
||||
}]).invoke(Browser.factory)
|
||||
|
||||
newBrowser.init()
|
||||
|
||||
if (config.singleRun) {
|
||||
newBrowser.execute()
|
||||
singleRunBrowsers.add(newBrowser)
|
||||
}
|
||||
}
|
||||
|
||||
replySocketEvents()
|
||||
})
|
||||
})
|
||||
|
||||
const emitRunCompleteIfAllBrowsersDone = () => {
|
||||
if (Object.keys(singleRunDoneBrowsers).every((key) => singleRunDoneBrowsers[key])) {
|
||||
this.emit('run_complete', singleRunBrowsers, singleRunBrowsers.getResults(singleRunBrowserNotCaptured, config))
|
||||
}
|
||||
}
|
||||
|
||||
this.on('browser_complete', (completedBrowser) => {
|
||||
if (completedBrowser.lastResult.disconnected && completedBrowser.disconnectsCount <= config.browserDisconnectTolerance) {
|
||||
this.log.info(`Restarting ${completedBrowser.name} (${completedBrowser.disconnectsCount} of ${config.browserDisconnectTolerance} attempts)`)
|
||||
|
||||
if (!launcher.restart(completedBrowser.id)) {
|
||||
this.emit('browser_restart_failure', completedBrowser)
|
||||
}
|
||||
} else {
|
||||
this.emit('browser_complete_with_no_more_retries', completedBrowser)
|
||||
}
|
||||
})
|
||||
|
||||
this.on('stop', (done) => {
|
||||
this.log.debug('Received stop event, exiting.')
|
||||
this._close()
|
||||
done()
|
||||
})
|
||||
|
||||
if (config.singleRun) {
|
||||
this.on('browser_restart_failure', (completedBrowser) => {
|
||||
singleRunDoneBrowsers[completedBrowser.id] = true
|
||||
emitRunCompleteIfAllBrowsersDone()
|
||||
})
|
||||
|
||||
// This is the normal exit trigger.
|
||||
this.on('browser_complete_with_no_more_retries', function (completedBrowser) {
|
||||
singleRunDoneBrowsers[completedBrowser.id] = true
|
||||
|
||||
if (launcher.kill(completedBrowser.id)) {
|
||||
completedBrowser.remove()
|
||||
}
|
||||
|
||||
emitRunCompleteIfAllBrowsersDone()
|
||||
})
|
||||
|
||||
this.on('browser_process_failure', (browserLauncher) => {
|
||||
singleRunDoneBrowsers[browserLauncher.id] = true
|
||||
singleRunBrowserNotCaptured = true
|
||||
|
||||
emitRunCompleteIfAllBrowsersDone()
|
||||
})
|
||||
|
||||
this.on('run_complete', (browsers, results) => {
|
||||
this.log.debug('Run complete, exiting.')
|
||||
this._close(results.exitCode)
|
||||
})
|
||||
|
||||
this.emit('run_start', singleRunBrowsers)
|
||||
}
|
||||
|
||||
if (config.autoWatch) {
|
||||
this.on('file_list_modified', () => {
|
||||
this.log.debug('List of files has changed, trying to execute')
|
||||
if (config.restartOnFileChange) {
|
||||
socketServer.sockets.emit('stop')
|
||||
}
|
||||
executor.schedule()
|
||||
})
|
||||
}
|
||||
|
||||
processWrapper.on('SIGINT', () => this._close())
|
||||
processWrapper.on('SIGTERM', () => this._close())
|
||||
|
||||
const reportError = (error) => {
|
||||
this.log.error(error)
|
||||
process.emit('infrastructure_error', error)
|
||||
this._close(1)
|
||||
}
|
||||
|
||||
processWrapper.on('unhandledRejection', (error) => {
|
||||
this.log.error(`UnhandledRejection: ${error.stack || error.message || String(error)}`)
|
||||
reportError(error)
|
||||
})
|
||||
|
||||
processWrapper.on('uncaughtException', (error) => {
|
||||
this.log.error(`UncaughtException: ${error.stack || error.message || String(error)}`)
|
||||
reportError(error)
|
||||
})
|
||||
}
|
||||
|
||||
_detach (config, done) {
|
||||
const tmpFile = tmp.fileSync({ keep: true })
|
||||
this.log.info('Starting karma detached')
|
||||
this.log.info('Run "karma stop" to stop the server.')
|
||||
this.log.debug(`Writing config to tmp-file ${tmpFile.name}`)
|
||||
config.detached = false
|
||||
try {
|
||||
fs.writeFileSync(tmpFile.name, JSON.stringify(config), 'utf8')
|
||||
} catch (e) {
|
||||
this.log.error("Couldn't write temporary configuration file")
|
||||
done(1)
|
||||
return
|
||||
}
|
||||
const child = spawn(process.argv[0], [path.resolve(__dirname, '../lib/detached.js'), tmpFile.name], {
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
})
|
||||
child.unref()
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup all resources allocated by Karma and call the `done` callback
|
||||
* with the result of the tests execution.
|
||||
*
|
||||
* @param [exitCode] - Optional exit code. If omitted will be computed by
|
||||
* 'exit' event listeners.
|
||||
*/
|
||||
_close (exitCode) {
|
||||
const webServer = this._injector.get('webServer')
|
||||
const socketServer = this._injector.get('socketServer')
|
||||
const done = this._injector.get('done')
|
||||
|
||||
const webServerCloseTimeout = 3000
|
||||
const sockets = socketServer.sockets.sockets
|
||||
|
||||
Object.keys(sockets).forEach((id) => {
|
||||
const socket = sockets[id]
|
||||
socket.removeAllListeners('disconnect')
|
||||
if (!socket.disconnected) {
|
||||
process.nextTick(socket.disconnect.bind(socket))
|
||||
}
|
||||
})
|
||||
|
||||
this.emitExitAsync(exitCode).catch((err) => {
|
||||
this.log.error('Error while calling exit event listeners\n' + err.stack || err)
|
||||
return 1
|
||||
}).then((code) => {
|
||||
socketServer.sockets.removeAllListeners()
|
||||
socketServer.close()
|
||||
|
||||
let removeAllListenersDone = false
|
||||
const removeAllListeners = () => {
|
||||
if (removeAllListenersDone) {
|
||||
return
|
||||
}
|
||||
removeAllListenersDone = true
|
||||
webServer.removeAllListeners()
|
||||
processWrapper.removeAllListeners()
|
||||
done(code || 0)
|
||||
}
|
||||
|
||||
const closeTimeout = setTimeout(removeAllListeners, webServerCloseTimeout)
|
||||
|
||||
webServer.close(() => {
|
||||
clearTimeout(closeTimeout)
|
||||
removeAllListeners()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
stop () {
|
||||
return this.emitAsync('stop')
|
||||
}
|
||||
}
|
||||
|
||||
Server.prototype._start.$inject = ['config', 'launcher', 'preprocess', 'fileList', 'capturedBrowsers', 'executor', 'done']
|
||||
|
||||
module.exports = Server
|
69
my-app/node_modules/karma/lib/stopper.js
generated
vendored
Executable file
69
my-app/node_modules/karma/lib/stopper.js
generated
vendored
Executable file
|
@ -0,0 +1,69 @@
|
|||
const http = require('http')
|
||||
const cfg = require('./config')
|
||||
const logger = require('./logger')
|
||||
const helper = require('./helper')
|
||||
const { lookup } = require('./utils/dns-utils')
|
||||
|
||||
exports.stop = function (cliOptionsOrConfig, done) {
|
||||
cliOptionsOrConfig = cliOptionsOrConfig || {}
|
||||
const log = logger.create('stopper')
|
||||
done = helper.isFunction(done) ? done : process.exit
|
||||
|
||||
let config
|
||||
if (cliOptionsOrConfig instanceof cfg.Config) {
|
||||
config = cliOptionsOrConfig
|
||||
} else {
|
||||
logger.setupFromConfig({
|
||||
colors: cliOptionsOrConfig.colors,
|
||||
logLevel: cliOptionsOrConfig.logLevel
|
||||
})
|
||||
const deprecatedCliOptionsMessage =
|
||||
'Passing raw CLI options to `stopper(config, done)` is deprecated. Use ' +
|
||||
'`parseConfig(configFilePath, cliOptions, {promiseConfig: true, throwErrors: true})` ' +
|
||||
'to prepare a processed `Config` instance and pass that as the ' +
|
||||
'`config` argument instead.'
|
||||
log.warn(deprecatedCliOptionsMessage)
|
||||
try {
|
||||
config = cfg.parseConfig(
|
||||
cliOptionsOrConfig.configFile,
|
||||
cliOptionsOrConfig,
|
||||
{
|
||||
promiseConfig: false,
|
||||
throwErrors: true
|
||||
}
|
||||
)
|
||||
} catch (parseConfigError) {
|
||||
// TODO: change how `done` falls back to exit in next major version
|
||||
// SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378
|
||||
done(1)
|
||||
}
|
||||
}
|
||||
|
||||
const request = http.request({
|
||||
hostname: config.hostname,
|
||||
path: config.urlRoot + 'stop',
|
||||
port: config.port,
|
||||
method: 'GET',
|
||||
lookup
|
||||
})
|
||||
|
||||
request.on('response', function (response) {
|
||||
if (response.statusCode === 200) {
|
||||
log.info('Server stopped.')
|
||||
done(0)
|
||||
} else {
|
||||
log.error(`Server returned status code: ${response.statusCode}`)
|
||||
done(1)
|
||||
}
|
||||
})
|
||||
|
||||
request.on('error', function (e) {
|
||||
if (e.code === 'ECONNREFUSED') {
|
||||
log.error(`There is no server listening on port ${config.port}`)
|
||||
done(1, e.code)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
request.end()
|
||||
}
|
31
my-app/node_modules/karma/lib/temp_dir.js
generated
vendored
Executable file
31
my-app/node_modules/karma/lib/temp_dir.js
generated
vendored
Executable file
|
@ -0,0 +1,31 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const fs = require('graceful-fs')
|
||||
const rimraf = require('rimraf')
|
||||
const log = require('./logger').create('temp-dir')
|
||||
|
||||
const TEMP_DIR = require('os').tmpdir()
|
||||
|
||||
module.exports = {
|
||||
getPath (suffix) {
|
||||
return path.normalize(TEMP_DIR + suffix)
|
||||
},
|
||||
|
||||
create (path) {
|
||||
log.debug(`Creating temp dir at ${path}`)
|
||||
|
||||
try {
|
||||
fs.mkdirSync(path)
|
||||
} catch (e) {
|
||||
log.warn(`Failed to create a temp dir at ${path}`)
|
||||
}
|
||||
|
||||
return path
|
||||
},
|
||||
|
||||
remove (path, done) {
|
||||
log.debug(`Cleaning temp dir ${path}`)
|
||||
rimraf(path, done)
|
||||
}
|
||||
}
|
31
my-app/node_modules/karma/lib/url.js
generated
vendored
Executable file
31
my-app/node_modules/karma/lib/url.js
generated
vendored
Executable file
|
@ -0,0 +1,31 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const { URL } = require('url')
|
||||
|
||||
/**
|
||||
* Url object used for tracking files in `file-list.js`.
|
||||
*/
|
||||
class Url {
|
||||
constructor (path, type, integrity) {
|
||||
this.path = path
|
||||
this.originalPath = path
|
||||
this.type = type
|
||||
this.integrity = integrity
|
||||
this.isUrl = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect type from the file extension in the path part of the URL.
|
||||
* @returns {string} detected file type or empty string
|
||||
*/
|
||||
detectType () {
|
||||
return path.extname(new URL(this.path).pathname).slice(1)
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.path
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Url
|
14
my-app/node_modules/karma/lib/utils/crypto-utils.js
generated
vendored
Executable file
14
my-app/node_modules/karma/lib/utils/crypto-utils.js
generated
vendored
Executable file
|
@ -0,0 +1,14 @@
|
|||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
|
||||
const CryptoUtils = {
|
||||
sha1 (data) {
|
||||
return crypto
|
||||
.createHash('sha1')
|
||||
.update(data)
|
||||
.digest('hex')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CryptoUtils
|
11
my-app/node_modules/karma/lib/utils/dns-utils.js
generated
vendored
Executable file
11
my-app/node_modules/karma/lib/utils/dns-utils.js
generated
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
const dns = require('dns')
|
||||
|
||||
// Node >=17 has different DNS resolution (see
|
||||
// https://github.com/nodejs/node/issues/40702), it resolves domains
|
||||
// according to the OS settings instead of IPv4-address first. The Karma server
|
||||
// only listens on IPv4 address (127.0.0.1) by default, but the requests are
|
||||
// sent to `localhost` in several places and `localhost` is resolved into IPv6
|
||||
// address (`::`). So the run/stop/proxy request is unable to reach the Karma
|
||||
// server and produces an error. To mitigate this issue karma force the
|
||||
// IPv4-address first approach in Node >=17 as well.
|
||||
module.exports.lookup = (hostname, options, callback) => dns.lookup(hostname, { ...options, verbatim: false }, callback)
|
25
my-app/node_modules/karma/lib/utils/file-utils.js
generated
vendored
Executable file
25
my-app/node_modules/karma/lib/utils/file-utils.js
generated
vendored
Executable file
|
@ -0,0 +1,25 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
|
||||
const FileUtils = {
|
||||
readFile (path) {
|
||||
return fs.readFileSync(path).toString()
|
||||
},
|
||||
|
||||
saveFile (path, content) {
|
||||
fs.writeFileSync(path, content)
|
||||
},
|
||||
|
||||
copyFile (src, dest) {
|
||||
FileUtils.saveFile(dest, FileUtils.readFile(src))
|
||||
},
|
||||
|
||||
removeFileIfExists (src) {
|
||||
if (fs.existsSync(src)) {
|
||||
fs.unlinkSync(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FileUtils
|
27
my-app/node_modules/karma/lib/utils/net-utils.js
generated
vendored
Executable file
27
my-app/node_modules/karma/lib/utils/net-utils.js
generated
vendored
Executable file
|
@ -0,0 +1,27 @@
|
|||
'use strict'
|
||||
|
||||
const net = require('net')
|
||||
|
||||
const NetUtils = {
|
||||
bindAvailablePort (port, listenAddress) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = net.createServer()
|
||||
|
||||
server
|
||||
.on('error', (err) => {
|
||||
server.close()
|
||||
if (err.code === 'EADDRINUSE' || err.code === 'EACCES') {
|
||||
server.listen(++port, listenAddress)
|
||||
} else {
|
||||
reject(new Error(`Failed to bind ${port}: ` + (err.stack || err)))
|
||||
}
|
||||
})
|
||||
.on('listening', () => {
|
||||
resolve(server)
|
||||
})
|
||||
.listen(port, listenAddress)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NetUtils
|
16
my-app/node_modules/karma/lib/utils/path-utils.js
generated
vendored
Executable file
16
my-app/node_modules/karma/lib/utils/path-utils.js
generated
vendored
Executable file
|
@ -0,0 +1,16 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
const PathUtils = {
|
||||
formatPathMapping (path, line, column) {
|
||||
return path + (line ? `:${line}` : '') + (column ? `:${column}` : '')
|
||||
},
|
||||
|
||||
calculateAbsolutePath (karmaRelativePath) {
|
||||
return path.join(__dirname, '..', '..', karmaRelativePath)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = PathUtils
|
14
my-app/node_modules/karma/lib/utils/pattern-utils.js
generated
vendored
Executable file
14
my-app/node_modules/karma/lib/utils/pattern-utils.js
generated
vendored
Executable file
|
@ -0,0 +1,14 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
const PatternUtils = {
|
||||
getBaseDir (pattern) {
|
||||
return pattern
|
||||
.replace(/[/\\][^/\\]*\*.*$/, '') // remove parts with *
|
||||
.replace(/[/\\][^/\\]*[!+]\(.*$/, '') // remove parts with !(...) and +(...)
|
||||
.replace(/[/\\][^/\\]*\)\?.*$/, '') || path.sep // remove parts with (...)?
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PatternUtils
|
85
my-app/node_modules/karma/lib/watcher.js
generated
vendored
Executable file
85
my-app/node_modules/karma/lib/watcher.js
generated
vendored
Executable file
|
@ -0,0 +1,85 @@
|
|||
'use strict'
|
||||
|
||||
const mm = require('minimatch')
|
||||
const braces = require('braces')
|
||||
const PatternUtils = require('./utils/pattern-utils')
|
||||
|
||||
const helper = require('./helper')
|
||||
const log = require('./logger').create('watcher')
|
||||
|
||||
const DIR_SEP = require('path').sep
|
||||
|
||||
function watchPatterns (patterns, watcher) {
|
||||
let expandedPatterns = []
|
||||
patterns.map((pattern) => {
|
||||
// expand ['a/{b,c}'] to ['a/b', 'a/c']
|
||||
expandedPatterns = expandedPatterns.concat(braces.expand(pattern, { keepEscaping: true }))
|
||||
})
|
||||
expandedPatterns
|
||||
.map(PatternUtils.getBaseDir)
|
||||
.filter((path, index, paths) => paths.indexOf(path) === index) // filter unique values
|
||||
.forEach((path, index, paths) => {
|
||||
if (!paths.some((p) => path.startsWith(p + DIR_SEP))) {
|
||||
watcher.add(path)
|
||||
log.debug(`Watching "${path}"`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function checkAnyPathMatch (patterns, path) {
|
||||
return patterns.some((pattern) => mm(path, pattern, { dot: true }))
|
||||
}
|
||||
|
||||
function createIgnore (patterns, excludes) {
|
||||
return function (path, stat) {
|
||||
if (stat && !stat.isDirectory()) {
|
||||
return !checkAnyPathMatch(patterns, path) || checkAnyPathMatch(excludes, path)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getWatchedPatterns (patterns) {
|
||||
return patterns
|
||||
.filter((pattern) => pattern.watched)
|
||||
.map((pattern) => pattern.pattern)
|
||||
}
|
||||
|
||||
function watch (patterns, excludes, fileList, usePolling, emitter) {
|
||||
const watchedPatterns = getWatchedPatterns(patterns)
|
||||
// Lazy-load 'chokidar' to make the dependency optional. This is desired when
|
||||
// third-party watchers are in use.
|
||||
const chokidar = require('chokidar')
|
||||
const watcher = new chokidar.FSWatcher({
|
||||
usePolling: usePolling,
|
||||
ignorePermissionErrors: true,
|
||||
ignoreInitial: true,
|
||||
ignored: createIgnore(watchedPatterns, excludes)
|
||||
})
|
||||
|
||||
watchPatterns(watchedPatterns, watcher)
|
||||
|
||||
watcher
|
||||
.on('add', (path) => fileList.addFile(helper.normalizeWinPath(path)))
|
||||
.on('change', (path) => fileList.changeFile(helper.normalizeWinPath(path)))
|
||||
.on('unlink', (path) => fileList.removeFile(helper.normalizeWinPath(path)))
|
||||
.on('error', log.debug.bind(log))
|
||||
|
||||
emitter.on('exit', (done) => {
|
||||
watcher.close()
|
||||
done()
|
||||
})
|
||||
|
||||
return watcher
|
||||
}
|
||||
|
||||
watch.$inject = [
|
||||
'config.files',
|
||||
'config.exclude',
|
||||
'fileList',
|
||||
'config.usePolling',
|
||||
'emitter'
|
||||
]
|
||||
|
||||
module.exports = watch
|
118
my-app/node_modules/karma/lib/web-server.js
generated
vendored
Executable file
118
my-app/node_modules/karma/lib/web-server.js
generated
vendored
Executable file
|
@ -0,0 +1,118 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
const path = require('path')
|
||||
const connect = require('connect')
|
||||
const mimeType = require('mime')
|
||||
|
||||
const common = require('./middleware/common')
|
||||
const runnerMiddleware = require('./middleware/runner')
|
||||
const stopperMiddleware = require('./middleware/stopper')
|
||||
const karmaMiddleware = require('./middleware/karma')
|
||||
const sourceFilesMiddleware = require('./middleware/source_files')
|
||||
const proxyMiddleware = require('./middleware/proxy')
|
||||
|
||||
const log = require('./logger').create('web-server')
|
||||
|
||||
function createCustomHandler (customFileHandlers, config) {
|
||||
let warningDone = false
|
||||
|
||||
return function (request, response, next) {
|
||||
const handler = customFileHandlers.find((handler) => handler.urlRegex.test(request.url))
|
||||
|
||||
if (customFileHandlers.length > 0 && !warningDone) {
|
||||
warningDone = true
|
||||
log.warn('The `customFileHandlers` is deprecated and will be removed in Karma 7. Please upgrade plugins relying on this provider.')
|
||||
}
|
||||
|
||||
return handler
|
||||
? handler.handler(request, response, 'fake/static', 'fake/adapter', config.basePath, 'fake/root')
|
||||
: next()
|
||||
}
|
||||
}
|
||||
|
||||
createCustomHandler.$inject = ['customFileHandlers', 'config']
|
||||
|
||||
function createFilesPromise (emitter, fileList) {
|
||||
// Set an empty list of files to avoid race issues with
|
||||
// file_list_modified not having been emitted yet
|
||||
let files = fileList.files
|
||||
emitter.on('file_list_modified', (filesParam) => { files = filesParam })
|
||||
|
||||
return {
|
||||
then (...args) {
|
||||
return Promise.resolve(files).then(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createServeStaticFile (config) {
|
||||
return common.createServeFile(fs, path.normalize(path.join(__dirname, '/../static')), config)
|
||||
}
|
||||
createServeStaticFile.$inject = ['config']
|
||||
|
||||
function createServeFile (config) {
|
||||
return common.createServeFile(fs, null, config)
|
||||
}
|
||||
createServeFile.$inject = ['config']
|
||||
|
||||
function createWebServer (injector, config) {
|
||||
const { mime = {} } = config
|
||||
mimeType.define({ ...mime }, true)
|
||||
|
||||
const proxyMiddlewareInstance = injector.invoke(proxyMiddleware.create)
|
||||
|
||||
log.debug('Instantiating middleware')
|
||||
const handler = connect()
|
||||
|
||||
if (config.beforeMiddleware) {
|
||||
config.beforeMiddleware.forEach((middleware) => handler.use(injector.get('middleware:' + middleware)))
|
||||
}
|
||||
|
||||
handler.use(injector.invoke(runnerMiddleware.create))
|
||||
handler.use(injector.invoke(stopperMiddleware.create))
|
||||
handler.use(injector.invoke(karmaMiddleware.create))
|
||||
handler.use(injector.invoke(sourceFilesMiddleware.create))
|
||||
// TODO(vojta): extract the proxy into a plugin
|
||||
handler.use(proxyMiddlewareInstance)
|
||||
// TODO: Deprecated. Remove in the next major
|
||||
handler.use(injector.invoke(createCustomHandler))
|
||||
|
||||
if (config.middleware) {
|
||||
config.middleware.forEach((middleware) => handler.use(injector.get('middleware:' + middleware)))
|
||||
}
|
||||
|
||||
handler.use((request, response) => common.serve404(response, request.url))
|
||||
|
||||
let serverClass = http
|
||||
const serverArguments = [handler]
|
||||
|
||||
if (config.protocol === 'https:') {
|
||||
serverClass = https
|
||||
serverArguments.unshift(config.httpsServerOptions || {})
|
||||
}
|
||||
|
||||
if (config.httpModule) {
|
||||
serverClass = config.httpModule
|
||||
}
|
||||
|
||||
const server = serverClass.createServer.apply(null, serverArguments)
|
||||
|
||||
server.on('upgrade', function (req, socket, head) {
|
||||
log.debug(`upgrade ${req.url}`)
|
||||
proxyMiddlewareInstance.upgrade(req, socket, head)
|
||||
})
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
createWebServer.$inject = ['injector', 'config']
|
||||
|
||||
module.exports = {
|
||||
createWebServer,
|
||||
createServeFile,
|
||||
createServeStaticFile,
|
||||
createFilesPromise
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue