🚀 Socket Launch Week 🚀 Day 3: Socket Acquires Coana.Learn More
Socket
Sign inDemoInstall
Socket

karma

Package Overview
Dependencies
Maintainers
5
Versions
213
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

karma - npm Package Compare versions

Comparing version

to
6.3.16

SECURITY.md

2

CODE_OF_CONDUCT.md

@@ -13,2 +13,2 @@ # Contributor Code of Conduct

This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.0.0, available at <https://www.contributor-covenant.org/version/1/0/0/code-of-conduct/>

@@ -1,1 +0,1 @@

module.exports = { extends: ['@commitlint/config-conventional'] }
module.exports = { extends: ['@commitlint/config-angular'] }

@@ -12,3 +12,3 @@ // Karma configuration

// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
// available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
frameworks: [%FRAMEWORKS%],

@@ -28,3 +28,3 @@

// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
// available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
preprocessors: {%PREPROCESSORS%

@@ -36,3 +36,3 @@ },

// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
// available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
reporters: ['progress'],

@@ -59,3 +59,3 @@

// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
// available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
browsers: [%BROWSERS%],

@@ -69,5 +69,5 @@

// Concurrency level
// how many browser should be started simultaneous
// how many browser instances should be started simultaneously
concurrency: Infinity
})
}

@@ -12,3 +12,3 @@ // Karma configuration

// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
// available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
frameworks: [%FRAMEWORKS%],

@@ -28,3 +28,3 @@

// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
// available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
preprocessors: {%PREPROCESSORS%

@@ -36,3 +36,3 @@ },

// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
// available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
reporters: ['progress'],

@@ -59,3 +59,3 @@

// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
// available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
browsers: [%BROWSERS%],

@@ -69,5 +69,5 @@

// Concurrency level
// how many browser should be started simultaneous
// how many browser instances should be started simultaneously
concurrency: Infinity
})
}

@@ -80,5 +80,5 @@ // Load our dependencies

}
// DEV: We must defined a function since we don't want to pass the event object
contextWindow.onbeforeunload = function (e, b) {
callParentKarmaMethod('onbeforeunload', [])
contextWindow.onbeforeunload = function () {
return self.error('Some of your tests did a full page reload!')
}

@@ -85,0 +85,0 @@

@@ -13,3 +13,3 @@ // Load in our dependencies

// DEV: We avoid using this in Internet Explorer as they only support strings
// http://caniuse.com/#search=postmessage
// https://caniuse.com/?search=postmessage
var haveParentAccess = false

@@ -16,0 +16,0 @@ try { haveParentAccess = !!parentWindow.window } catch (err) { /* Ignore errors (likely permisison errors) */ }

@@ -14,3 +14,4 @@ 'use strict'

class Browser {
constructor (id, fullName, collection, emitter, socket, timer, disconnectDelay, noActivityTimeout) {
constructor (id, fullName, collection, emitter, socket, timer, disconnectDelay,
noActivityTimeout, singleRun, clientConfig) {
this.id = id

@@ -23,2 +24,4 @@ this.fullName = fullName

this.noActivityTimeout = noActivityTimeout
this.singleRun = singleRun
this.clientConfig = clientConfig
this.collection = collection

@@ -99,5 +102,4 @@ this.emitter = emitter

onDisconnect (reason, disconnectedSocket) {
onSocketDisconnect (reason, disconnectedSocket) {
helper.arrayRemove(this.activeSockets, disconnectedSocket)
if (this.activeSockets.length) {

@@ -125,4 +127,17 @@ this.log.debug(`Disconnected ${disconnectedSocket.id}, still have ${this.getActiveSocketsIds()}`)

reconnect (newSocket) {
if (this.state === EXECUTING_DISCONNECTED) {
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 ' +

@@ -134,12 +149,2 @@ `on socket ${newSocket.id}.`)

`${this.getActiveSocketsIds()})`)
} else if (this.state === DISCONNECTED) {
this.log.info(`Disconnected browser returned on socket ${newSocket.id} with id ${this.id}.`)
this.setState(CONNECTED)
// Since the disconnected browser is already part of the collection and we want to
// make sure that the server can properly handle the browser like it's the first time
// connecting this browser (as we want a complete new execution), we need to emit the
// following events:
this.emitter.emit('browsers_change', this.collection)
this.emitter.emit('browser_register', this)
}

@@ -169,4 +174,4 @@

execute (config) {
this.activeSockets.forEach((socket) => socket.emit('execute', config))
execute () {
this.activeSockets.forEach((socket) => socket.emit('execute', this.clientConfig))
this.setState(CONFIGURING)

@@ -181,6 +186,10 @@ this.refreshNoActivityTimeout()

disconnect (reason) {
this.log.warn(`Disconnected (${this.disconnectsCount} times)${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.disconnectsCount++
this.emitter.emit('browser_error', this, `Disconnected${reason || ''}`)
this.collection.remove(this)

@@ -211,3 +220,3 @@ }

// TODO: check which of these events are actually emitted by socket
socket.on('disconnect', (reason) => this.onDisconnect(reason, socket))
socket.on('disconnect', (reason) => this.onSocketDisconnect(reason, socket))
socket.on('start', (info) => this.onStart(info))

@@ -257,5 +266,7 @@ socket.on('karma_error', (error) => this.onKarmaError(error))

/* config.browserDisconnectTimeout */ disconnectDelay,
/* config.browserNoActivityTimeout */ noActivityTimeout
) {
return new Browser(id, fullName, collection, emitter, socket, timer, disconnectDelay, noActivityTimeout)
/* config.browserNoActivityTimeout */ noActivityTimeout,
/* config.singleRun */ singleRun,
/* config.client */ clientConfig) {
return new Browser(id, fullName, collection, emitter, socket, timer,
disconnectDelay, noActivityTimeout, singleRun, clientConfig)
}

@@ -262,0 +273,0 @@

'use strict'
const path = require('path')
const assert = require('assert')
const yargs = require('yargs')

@@ -11,10 +10,8 @@ const fs = require('graceful-fs')

const constant = require('./constants')
const cfg = require('./config')
function processArgs (argv, options, fs, path) {
// TODO(vojta): warn/throw when unknown argument (probably mispelled)
Object.getOwnPropertyNames(argv).forEach(function (name) {
let argumentValue = argv[name]
if (name !== '_' && name !== '$0') {
assert(!name.includes('_'), `Bad argument: ${name} did you mean ${name.replace('_', '-')}`)
if (Array.isArray(argumentValue)) {

@@ -103,3 +100,3 @@ argumentValue = argumentValue.pop() // If the same argument is defined multiple times, override.

let configFile = argv._.shift()
let configFile = argv.configFile

@@ -156,6 +153,6 @@ if (!configFile) {

' $0 <command>')
.command('init', 'Initialize a config file.', describeInit)
.command('start', 'Start the server / do a single run.', describeStart)
.command('run', 'Trigger a test run.', describeRun)
.command('stop', 'Stop the server.', describeStop)
.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)

@@ -176,2 +173,6 @@ .demandCommand(1, 'Command not specified.')

.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.')

@@ -190,2 +191,6 @@ .describe('colors', 'Use colors when reporting and printing logs.')

.version(false)
.positional('configFile', {
describe: 'Path to the Karma configuration file',
type: 'string'
})
.describe('port', '<integer> Port where the server is running.')

@@ -208,2 +213,6 @@ .describe('auto-watch', 'Auto watch source files and run on change.')

.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'
})
}

@@ -219,2 +228,6 @@

.version(false)
.positional('configFile', {
describe: 'Path to the Karma configuration file',
type: 'string'
})
.describe('port', '<integer> Port where the server is listening.')

@@ -227,2 +240,14 @@ .describe('no-refresh', 'Do not re-glob all the patterns.')

.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'
})
}

@@ -238,2 +263,6 @@

.version(false)
.positional('configFile', {
describe: 'Path to the Karma configuration file',
type: 'string'
})
.describe('port', '<integer> Port where the server is listening.')

@@ -249,3 +278,2 @@ .describe('log-level', '<disable | error | warn | info | debug> Level of logging.')

' $0 completion >> ~/.bashrc')
.strictCommands(false)
.version(false)

@@ -263,23 +291,46 @@ }

exports.run = () => {
const config = exports.process()
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.
switch (config.cmd) {
case 'start':
new Server(config).start()
break
case 'run':
require('./runner')
.run(config)
.on('progress', printRunnerProgress)
break
case 'stop':
require('./stopper').stop(config)
break
case 'init':
require('./init').init(config)
break
case 'completion':
require('./completion').completion(config)
break
// 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)
}
}

@@ -286,0 +337,0 @@ }

@@ -20,3 +20,3 @@ 'use strict'

COFFEE_SCRIPT_AVAILABLE = true
} catch (e) {}
} catch {}

@@ -28,8 +28,8 @@ // LiveScript is required here to enable config files written in LiveScript.

LIVE_SCRIPT_AVAILABLE = true
} catch (e) {}
} catch {}
try {
require('ts-node').register()
require('ts-node')
TYPE_SCRIPT_AVAILABLE = true
} catch (e) {}
} catch {}

@@ -231,3 +231,3 @@ class Pattern {

// define custom launchers/preprocessors/reporters - create an inlined plugin
// define custom launchers/preprocessors/reporters - create a new plugin
const module = Object.create(null)

@@ -274,2 +274,5 @@ let hasSomeInlinedPlugin = false

/**
* @class
*/
class Config {

@@ -328,3 +331,4 @@ constructor () {

captureConsole: true,
clearContext: true
clearContext: true,
allowedReturnUrlPatterns: ['^https?://']
}

@@ -359,6 +363,77 @@ this.browserDisconnectTimeout = 2000

function parseConfig (configFilePath, cliOptions) {
/**
* 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)

@@ -369,4 +444,2 @@ if (typeof configModule === 'object' && typeof configModule.default !== 'undefined') {

} catch (e) {
log.error('Error in config file!\n ' + e.stack || e)
const extension = path.extname(configFilePath)

@@ -380,7 +453,6 @@ if (extension === '.coffee' && !COFFEE_SCRIPT_AVAILABLE) {

}
return process.exit(1)
return fail('Error in config file!\n ' + e.stack || e)
}
if (!helper.isFunction(configModule)) {
log.error('Config file must export a function!\n' + CONFIG_SYNTAX_HELP)
return process.exit(1)
return fail('Config file must export a function!\n' + CONFIG_SYNTAX_HELP)
}

@@ -403,31 +475,72 @@ } else {

let configModuleReturn
try {
configModule(config)
configModuleReturn = configModule(config)
} catch (e) {
log.error('Error in config file!\n', e)
return process.exit(1)
return fail('Error in config file!\n', e)
}
function finalizeConfig (config) {
// merge the config from config file and cliOptions (precedence)
config.set(cliOptions)
// 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: ` +
// 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
}
}
// 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)
// 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.')
log.debug(configFilePath ? `Loading config ${configFilePath}` : 'No config file specified.')
return normalizeConfig(config, configFilePath)
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)
}
}
}

@@ -434,0 +547,0 @@

@@ -31,4 +31,4 @@ 'use strict'

// Default patterns for the pattern layout.
exports.COLOR_PATTERN = '%[%d{DATE}:%p [%c]: %]%m'
exports.NO_COLOR_PATTERN = '%d{DATE}:%p [%c]: %m'
exports.COLOR_PATTERN = '%[%d{DATETIME}:%p [%c]: %]%m'
exports.NO_COLOR_PATTERN = '%d{DATETIME}:%p [%c]: %m'

@@ -35,0 +35,0 @@ // Default console appender

'use strict'
const fs = require('graceful-fs')
const path = require('path')
const _ = require('lodash')
const mkdirp = require('mkdirp')
const useragent = require('ua-parser-js')

@@ -144,14 +143,3 @@ const mm = require('minimatch')

exports.mkdirIfNotExists = (directory, done) => {
// TODO(vojta): handle if it's a file
/* eslint-disable handle-callback-err */
fs.stat(directory, (err, stat) => {
if (stat && stat.isDirectory()) {
done()
} else {
exports.mkdirIfNotExists(path.dirname(directory), () => {
fs.mkdir(directory, done)
})
}
})
/* eslint-enable handle-callback-err */
mkdirp(directory, done)
}

@@ -158,0 +146,0 @@

@@ -10,14 +10,2 @@ 'use strict'

// TODO: remove in 1.0
const oldServer = {
start: function (cliOptions, done) {
console.error('WARN `start` method is deprecated since 0.13. It will be removed in 0.14. Please use \n' +
' server = new Server(config, [done])\n' +
' server.start()\n' +
'instead.')
const server = new Server(cliOptions, done)
server.start()
}
}
module.exports = {

@@ -30,4 +18,3 @@ constants: constants,

launcher: launcher,
config: { parseConfig: cfg.parseConfig }, // lets start with only opening up the `parseConfig` api
server: oldServer
config: { parseConfig: cfg.parseConfig } // lets start with only opening up the `parseConfig` api
}

@@ -52,3 +52,3 @@ 'use strict'

} 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.`)
log.warn(`Failed to install "${pkgName}". It is not in the npm registry!\n Please install it manually.`)
} else if (/Error: EACCES/.test(stderr)) {

@@ -55,0 +55,0 @@ log.warn(`Failed to install "${pkgName}". No permissions to write in ${options.cwd}!\n Please install it manually.`)

@@ -20,3 +20,3 @@ const log = require('../logger').create('launcher')

log.warn(`${this.name} have not captured in ${captureTimeout} ms, killing.`)
log.warn(`${this.name} has not captured in ${captureTimeout} ms, killing.`)
this.error = 'timeout'

@@ -23,0 +23,0 @@ this.kill()

@@ -96,2 +96,3 @@ const path = require('path')

}
self._onProcessExit(-1, null, errorOutput)
})

@@ -105,2 +106,6 @@

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}`)

@@ -107,0 +112,0 @@

@@ -23,3 +23,2 @@ /**

js: 'text/javascript',
dart: 'application/dart',
module: 'module'

@@ -31,3 +30,2 @@ }

'js',
'dart',
'module',

@@ -52,3 +50,3 @@ 'dom'

if (query['x-ua-compatible']) {
return `\n<meta http-equiv="X-UA-Compatible" content="${query['x-ua-compatible']}"/>`
return `<meta http-equiv="X-UA-Compatible" content="${query['x-ua-compatible']}"/>`
}

@@ -115,3 +113,3 @@ return ''

data
.replace('\n%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
.replace('%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
.replace('%X_UA_COMPATIBLE_URL%', getXUACompatibleUrl(request.url)))

@@ -176,3 +174,3 @@ }

` To silence the warning specify a valid type for ${file.originalPath} in the configuration file.\n` +
' See http://karma-runner.github.io/latest/config/files.html'
' See https://karma-runner.github.io/latest/config/files.html'
)

@@ -201,3 +199,7 @@ } else {

const crossOriginAttribute = includeCrossOriginAttribute ? 'crossorigin="anonymous"' : ''
scriptTags.push(`<script type="${scriptType}" src="${filePath}" ${crossOriginAttribute}></script>`)
if (fileType === 'module') {
scriptTags.push(`<script onerror="throw 'Error loading ${filePath}'" type="${scriptType}" src="${filePath}" ${crossOriginAttribute}></script>`)
} else {
scriptTags.push(`<script type="${scriptType}" src="${filePath}" ${crossOriginAttribute}></script>`)
}
}

@@ -229,7 +231,7 @@ }

return data
.replace('%SCRIPTS%', scriptTags.join('\n'))
.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('\n%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
.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))
})

@@ -236,0 +238,0 @@ })

@@ -37,16 +37,12 @@ const url = require('url')

const protocol = proxyDetails.protocol || config.protocol
const https = proxyDetails.protocol === 'https:'
let port
if (proxyDetails.port) {
port = proxyDetails.port
} else if (proxyDetails.protocol) {
port = https ? '443' : '80'
} else {
port = config.port
const defaultPorts = {
'http:': '80',
'https:': '443'
}
const port = proxyDetails.port || defaultPorts[proxyDetails.protocol] || config.port
const changeOrigin = proxyConfiguration.changeOrigin || false
const Agent = https ? httpsAgent : httpAgent
const Agent = protocol === 'https:' ? httpsAgent : httpAgent
const agent = new Agent({ keepAlive: true })
const proxy = httpProxy.createProxyServer({
target: { host: hostname, port, https, protocol },
target: { host: hostname, port, protocol },
xfwd: true,

@@ -75,3 +71,3 @@ changeOrigin: changeOrigin,

return { path: proxyPath, baseUrl: pathname, host: hostname, port, https, proxy, agent }
return { path: proxyPath, baseUrl: pathname, host: hostname, port, proxy, agent }
}), 'path').reverse()

@@ -78,0 +74,0 @@ }

@@ -35,10 +35,19 @@ 'use strict'

const pluginDirectory = path.normalize(path.join(__dirname, '/../..'))
const regexp = new RegExp(`^${plugin.replace('*', '.*')}`)
const regexp = new RegExp(`^${plugin.replace(/\*/g, '.*').replace(/\//g, '[/\\\\]')}`)
log.debug(`Loading ${plugin} from ${pluginDirectory}`)
fs.readdirSync(pluginDirectory)
.filter((pluginName) => !IGNORED_PACKAGES.includes(pluginName) && regexp.test(pluginName))
.forEach((pluginName) => requirePlugin(`${pluginDirectory}/${pluginName}`))
.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 inlined plugin (defining ${Object.keys(plugin).join(', ')}).`)
log.debug(`Loading inline plugin defining ${Object.keys(plugin).join(', ')}.`)
modules.push(plugin)

@@ -54,2 +63,38 @@ } else {

exports.resolve = resolve
/**
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 }

@@ -73,32 +73,4 @@ 'use strict'

function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, injector) {
const emitter = injector.get('emitter')
const instances = new Map()
function instantiatePreprocessor (name) {
if (instances.has(name)) {
return instances.get(name)
}
let p
try {
p = injector.get('preprocessor:' + name)
if (!p) {
log.error(`Failed to instantiate preprocessor ${name}`)
emitter.emit('load_error', 'preprocessor', name)
}
} catch (e) {
if (e.message.includes(`No provider for "preprocessor:${name}"`)) {
log.error(`Can not load "${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', 'preprocessor', name)
}
instances.set(name, p)
return p
}
_.union.apply(_, Object.values(config)).forEach(instantiatePreprocessor)
function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, instantiatePlugin) {
_.union.apply(_, Object.values(config)).forEach((name) => instantiatePlugin('preprocessor', name))
return async function preprocess (file) {

@@ -125,3 +97,3 @@ const buffer = await tryToRead(file.originalPath, log)

.reduce((preProcs, name) => {
const p = instantiatePreprocessor(name)
const p = instantiatePlugin('preprocessor', name)

@@ -140,3 +112,3 @@ if (!isBinary || (p && p.handleBinaryFiles)) {

createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'injector']
createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'instantiatePlugin']
exports.createPriorityPreprocessor = createPriorityPreprocessor

@@ -51,3 +51,3 @@ 'use strict'

let msg = input.replace(URL_REGEXP, function (_, prefix, path, __, ___, line, ____, column) {
let msg = input.replace(URL_REGEXP, function (stackTracePath, prefix, path, __, ___, line, ____, column) {
const normalizedPath = prefix === 'base/' ? `${basePath}/${path}` : path

@@ -68,2 +68,10 @@ const file = lastServedFiles.find((file) => file.path === normalizedPath)

// 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

@@ -74,3 +82,4 @@ // the path is not absolute yet.

} catch (e) {
log.warn(`SourceMap position not found for trace: ${input}`)
log.warn(`An unexpected error occurred while resolving the original position for: ${stackTracePath}`)
log.warn(e)
}

@@ -77,0 +86,0 @@ }

@@ -35,10 +35,35 @@ 'use strict'

// TODO(vojta): read config file (port, host, urlRoot)
function run (config, done) {
config = config || {}
logger.setupFromConfig(config)
function run (cliOptionsOrConfig, done) {
cliOptionsOrConfig = cliOptionsOrConfig || {}
done = helper.isFunction(done) ? done : process.exit
config = cfg.parseConfig(config.configFile, config)
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

@@ -45,0 +70,0 @@ const emitter = new EventEmitter()

@@ -39,3 +39,3 @@ 'use strict'

function createSocketIoServer (webServer, executor, config) {
const server = new SocketIO(webServer, {
const server = new SocketIO.Server(webServer, {
// avoid destroying http upgrades from socket.io to get proxied websockets working

@@ -46,4 +46,6 @@ destroyUpgrade: false,

forceJSONP: config.forceJSONP,
// Default is 5000 in socket.io v2.x.
pingTimeout: config.pingTimeout || 5000
// 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
})

@@ -58,14 +60,47 @@

class Server extends KarmaEventEmitter {
constructor (cliOptions, done) {
constructor (cliOptionsOrConfig, done) {
super()
logger.setupFromConfig(cliOptions)
cliOptionsOrConfig = cliOptionsOrConfig || {}
this.log = logger.create('karma-server')
done = helper.isFunction(done) ? done : process.exit
this.loadErrors = []
const config = cfg.parseConfig(cliOptions.configFile, cliOptions)
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 = [{

@@ -80,2 +115,3 @@ helper: ['value', helper],

config: ['value', config],
instantiatePlugin: ['factory', plugin.createInstantiatePlugin],
preprocess: ['factory', preprocessor.createPriorityPreprocessor],

@@ -89,6 +125,4 @@ fileList: ['factory', FileList.factory],

executor: ['factory', Executor.factory],
// TODO(vojta): remove
// TODO: Deprecated. Remove in the next major
customFileHandlers: ['value', []],
// TODO(vojta): remove, once karma-dart does not rely on it
customScriptTypes: ['value', []],
reporter: ['factory', reporter.createReporters],

@@ -114,8 +148,2 @@ capturedBrowsers: ['factory', BrowserCollection.factory],

dieOnError (error) {
this.log.error(error)
process.exitCode = 1
process.kill(process.pid, 'SIGINT')
}
async start () {

@@ -135,3 +163,4 @@ const config = this.get('config')

} catch (err) {
this.dieOnError(`Server start failed on port ${config.port}: ${err}`)
this.log.error(`Server start failed on port ${config.port}: ${err}`)
this._close(1)
}

@@ -201,3 +230,4 @@ }

webServer.on('error', (err) => {
this.dieOnError(`Webserver fail ${err}`)
this.log.error(`Webserver fail ${err}`)
this._close(1)
})

@@ -221,3 +251,4 @@

if (this.loadErrors.length > 0) {
this.dieOnError(new Error(`Found ${this.loadErrors.length} load error${this.loadErrors.length === 1 ? '' : 's'}`))
this.log.error(new Error(`Found ${this.loadErrors.length} load error${this.loadErrors.length === 1 ? '' : 's'}`))
this._close(1)
}

@@ -250,2 +281,4 @@ })

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}`)

@@ -273,4 +306,2 @@ const browserLogFile = fs.openSync(configPath, 'w+')

socket.on('complete', (data, ack) => ack())
socket.on('error', (err) => {

@@ -281,23 +312,8 @@ this.log.debug('karma server socket error: ' + err)

socket.on('register', (info) => {
let newBrowser = info.id ? (capturedBrowsers.getById(info.id) || singleRunBrowsers.getById(info.id)) : null
const knownBrowser = info.id ? (capturedBrowsers.getById(info.id) || singleRunBrowsers.getById(info.id)) : null
if (newBrowser) {
// By default if a browser disconnects while still executing, we assume that the test
// execution still continues because just the socket connection has been terminated. Now
// since we know whether this is just a socket reconnect or full client reconnect, we
// need to update the browser state accordingly. This is necessary because in case a
// browser crashed and has been restarted, we need to start with a fresh execution.
if (!info.isSocketReconnect) {
newBrowser.setState(Browser.STATE_DISCONNECTED)
}
newBrowser.reconnect(socket)
// Since not every reconnected browser is able to continue with its previous execution,
// we need to start a new execution in case a browser has restarted and is now idling.
if (newBrowser.state === Browser.STATE_CONNECTED && config.singleRun) {
newBrowser.execute(config.client)
}
if (knownBrowser) {
knownBrowser.reconnect(socket, info.isSocketReconnect)
} else {
newBrowser = this._injector.createChild([{
const newBrowser = this._injector.createChild([{
id: ['value', info.id || null],

@@ -311,3 +327,3 @@ fullName: ['value', (helper.isDefined(info.displayName) ? info.displayName : info.name)],

if (config.singleRun) {
newBrowser.execute(config.client)
newBrowser.execute()
singleRunBrowsers.add(newBrowser)

@@ -339,5 +355,5 @@ }

this.on('stop', function (done) {
this.on('stop', (done) => {
this.log.debug('Received stop event, exiting.')
disconnectBrowsers()
this._close()
done()

@@ -357,4 +373,3 @@ })

if (launcher.kill(completedBrowser.id)) {
// workaround to supress "disconnect" warning
completedBrowser.state = Browser.STATE_DISCONNECTED
completedBrowser.remove()
}

@@ -372,5 +387,5 @@

this.on('run_complete', function (browsers, results) {
this.on('run_complete', (browsers, results) => {
this.log.debug('Run complete, exiting.')
disconnectBrowsers(results.exitCode)
this._close(results.exitCode)
})

@@ -391,52 +406,13 @@

const webServerCloseTimeout = 3000
const disconnectBrowsers = (code) => {
const sockets = socketServer.sockets.sockets
processWrapper.on('SIGINT', () => this._close())
processWrapper.on('SIGTERM', () => this._close())
Object.keys(sockets).forEach((id) => {
const socket = sockets[id]
socket.removeAllListeners('disconnect')
if (!socket.disconnected) {
process.nextTick(socket.disconnect.bind(socket))
}
})
this.emitExitAsync(code).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()
})
})
}
processWrapper.on('SIGINT', () => disconnectBrowsers(process.exitCode))
processWrapper.on('SIGTERM', disconnectBrowsers)
const reportError = (error) => {
this.log.error(error)
process.emit('infrastructure_error', error)
disconnectBrowsers(1)
this.log.error(error)
this._close(1)
}
processWrapper.on('unhandledRejection', (error) => {
this.log.error(`UnhandledRejection: ${error.message || String(error)}`)
this.log.error(`UnhandledRejection: ${error.stack || error.message || String(error)}`)
reportError(error)

@@ -446,3 +422,3 @@ })

processWrapper.on('uncaughtException', (error) => {
this.log.error(`UncaughtException:: ${error.message || String(error)}`)
this.log.error(`UncaughtException: ${error.stack || error.message || String(error)}`)
reportError(error)

@@ -472,10 +448,55 @@ })

/**
* 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')
}
static start (cliOptions, done) {
console.warn('Deprecated static method to be removed in v3.0')
return new Server(cliOptions, done).start()
}
}

@@ -482,0 +503,0 @@

@@ -6,9 +6,37 @@ const http = require('http')

exports.stop = function (config, done) {
config = config || {}
logger.setupFromConfig(config)
exports.stop = function (cliOptionsOrConfig, done) {
cliOptionsOrConfig = cliOptionsOrConfig || {}
const log = logger.create('stopper')
done = helper.isFunction(done) ? done : process.exit
config = cfg.parseConfig(config.configFile, config)
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({

@@ -15,0 +43,0 @@ hostname: config.hostname,

@@ -20,4 +20,12 @@ 'use strict'

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

@@ -73,4 +81,3 @@ ? handler.handler(request, response, 'fake/static', 'fake/adapter', config.basePath, 'fake/root')

handler.use(proxyMiddlewareInstance)
// TODO(vojta): remove, this is only here because of karma-dart
// we need a better way of custom handlers
// TODO: Deprecated. Remove in the next major
handler.use(injector.invoke(createCustomHandler))

@@ -77,0 +84,0 @@

{
"name": "karma",
"description": "Spectacular Test Runner for JavaScript.",
"homepage": "http://karma-runner.github.io/",
"homepage": "https://karma-runner.github.io/",
"repository": {

@@ -33,5 +33,5 @@ "type": "git",

"Maksim Ryzhikov <rv.maksim@gmail.com>",
"semantic-release-bot <semantic-release-bot@martynus.net>",
"ukasz Usarz <lukasz.usarz@gmail.com>",
"Christian Budde Christensen <budde377@gmail.com>",
"semantic-release-bot <semantic-release-bot@martynus.net>",
"Wesley Cho <wesley.cho@gmail.com>",

@@ -42,2 +42,3 @@ "taichi <ryushi@gmail.com>",

"Anton <anton.redfox@gmail.com>",
"Jonathan Ginsburg <jon@than.ml>",
"Michał Gołębiowski-Owczarek <m.goleb@gmail.com>",

@@ -76,2 +77,3 @@ "Todd Wolfson <todd@twolfson.com>",

"Pieter Mees <pietermees@users.noreply.github.com>",
"Sergei Startsev <ai@programist.ru>",
"pavelgj <pavelgj@gmail.com>",

@@ -85,2 +87,3 @@ "sylvain-hamel <sylvainhamel0@gmail.com>",

"ChangZhuo Chen (陳昌倬) <czchen@gmail.com>",
"Chris Bottin <chrisbottin@users.noreply.github.com>",
"Cyrus Chan <chan1cyrus2@gmail.com>",

@@ -100,2 +103,3 @@ "DarthCharles <carlos.darth@gmail.com>",

"James Talmage <james@talmage.io>",
"Janderson Constantino <jandersonconstantino@gmail.com>",
"Jonas Pommerening <jonas.pommerening@aixigo.de>",

@@ -116,2 +120,3 @@ "Jonathan Freeman <freethejazz@gmail.com>",

"PatrickJS <github@gdi2290.com>",
"Paul Gschwendtner <paulgschwendtner@gmail.com>",
"Richard Harrington <rwharrington87@gmail.com>",

@@ -124,3 +129,2 @@ "Roarke Gaskill <roarke.gaskill@gmail.com>",

"Sammy Jelin <sjelin@gmail.com>",
"Sergei Startsev <ai@programist.ru>",
"Sergey Simonchik <sergey.simonchik@gmail.com>",

@@ -134,2 +138,3 @@ "Sergey Simonchik <sergey.simonchik@jetbrains.com>",

"Sylvain Hamel <sylvainhamel0@gmail.com>",
"SymbioticKilla <59652865+SymbioticKilla@users.noreply.github.com>",
"Terry <zhangwenlp@vip.qq.com>",

@@ -163,2 +168,3 @@ "Thomas Parisot <thomas@oncle-tom.net>",

"Anton Usmansky <cody0@yandex-team.ru>",
"Athur Ming <mingguobin@live.com>",
"Atul Bhosale <atul1bhosale@gmail.com>",

@@ -182,2 +188,3 @@ "AugustinLF <augustin.public@gmail.com>",

"Chang Wang <cheapsteak@gmail.com>",
"Charles Suh <charlessuh@users.noreply.github.com>",
"Chelsea Urquhart <curquhart@users.noreply.github.com>",

@@ -217,2 +224,3 @@ "Chris <camargo.cac@gmail.com>",

"Esteban Marin <estebanmarin@gmx.ch>",
"Evgeniy Chekan <hom3chuk@gmail.com>",
"Fabian Beuke <mail@beuke.org>",

@@ -238,3 +246,2 @@ "Filipe Silva <filipematossilva@gmail.com>",

"Jan Molak <jan.molak@smartcodeltd.co.uk>",
"Janderson Constantino <jandersonconstantino@gmail.com>",
"Jeff Froom <jeff@jfroom.com>",

@@ -268,3 +275,5 @@ "Jeff Lage <jefflage@me.com>",

"Kris Kowal <kris@cixar.com>",
"Lachlan Heywood <lachieh@users.noreply.github.com>",
"Lenny Urbanowski <lenny@itslennysfault.com>",
"Long Ho <longlho@users.noreply.github.com>",
"LoveIsGrief <just.another.michaelv@gmail.com>",

@@ -282,2 +291,3 @@ "Lucas Theisen <lucastheisen@pastdev.com>",

"Martin Probst <martin@probst.io>",
"Marvin Heilemann <m.heilemann@protonmail.com>",
"Matias Niemelä <matias@yearofmoo.com>",

@@ -290,2 +300,3 @@ "Matthew Amato <matt.amato@gmail.com>",

"Mattijs Kneppers <mattijs@arttech.nl>",
"Max Rose <max.rose366@gmail.com>",
"Max Waterman <davidmaxwaterman@fastmail.co.uk>",

@@ -295,2 +306,3 @@ "Merott Movahedi <merott@merott.com>",

"Michael Krotscheck <krotscheck@gmail.com>",
"Michael Vartan <michael.wayne.vartan@gmail.com>",
"Michał Siwek <mike21@aol.pl>",

@@ -306,2 +318,4 @@ "Milan Aleksic <milanaleksic@gmail.com>",

"Nick Payne <nick@kurai.co.uk>",
"Nick Petruzzelli <code.npetruzzelli@gmail.com>",
"Nick Petruzzelli <npetruzzelli@users.noreply.github.com>",
"Nick Williams <mr.nicksta@gmail.com>",

@@ -324,3 +338,2 @@ "Nicolas Artman <nicolasartman@users.noreply.github.com>",

"Patrik Henningsson <patrik.henningsson@gmail.com>",
"Paul Gschwendtner <paulgschwendtner@gmail.com>",
"Paweł Kapalla <pkapalla@xesenix.pl>",

@@ -343,2 +356,3 @@ "Pedro Araujo <pedrotcaraujo@gmail.com>",

"Rich Kuzsma <rkuzsma@gmail.com>",
"Rich Trott <rtrott@gmail.com>",
"Richard Herrera <richard.herrera@nfl.com>",

@@ -368,2 +382,3 @@ "Roarke Gaskill <rgaskill@nexvex.com>",

"Sophie Cooper <scooper91@users.noreply.github.com>",
"Stefan Becking <sb@web-computing.de>",
"Stephen Hazleton <shazleto@gmail.com>",

@@ -391,2 +406,3 @@ "Stuart Memo <stuartmemo@gmail.com>",

"Wizek <123.wizek@gmail.com>",
"XhmikosR <xhmikosr@gmail.com>",
"Yaniv Efraim <yaniv.efraim@gmail.com>",

@@ -408,2 +424,3 @@ "Yi Wang <e@yi-wang.me>",

"grifball <scottgriffy@gmail.com>",
"hdmr14 <58992133+hdmr14@users.noreply.github.com>",
"hrgdavor <hrgdavor@gmail.com>",

@@ -415,2 +432,3 @@ "ianjobling <ijobling@codio.com>",

"jvalkeejarvi <jvalkeejarvi@gmail.com>",
"katrina95 <34797724+katrina95@users.noreply.github.com>",
"kyo_ago <kyo.ago@gmail.com>",

@@ -426,36 +444,40 @@ "lanshunfang <lanshunfang@gmail.com>",

"toran billups <toranb@gmail.com>",
"xel23 <sawqe1@yandex.ru>",
"chalkerx@gmail.com>",
"weiran.zsd@outlook.com>"
],
"overrides": {
"colors": "1.4.0"
},
"dependencies": {
"body-parser": "^1.19.0",
"braces": "^3.0.2",
"chokidar": "^3.4.2",
"colors": "^1.4.0",
"chokidar": "^3.5.1",
"colors": "1.4.0",
"connect": "^3.7.0",
"di": "^0.0.1",
"dom-serialize": "^2.2.1",
"glob": "^7.1.6",
"graceful-fs": "^4.2.4",
"glob": "^7.1.7",
"graceful-fs": "^4.2.6",
"http-proxy": "^1.18.1",
"isbinaryfile": "^4.0.6",
"lodash": "^4.17.19",
"log4js": "^6.2.1",
"mime": "^2.4.5",
"isbinaryfile": "^4.0.8",
"lodash": "^4.17.21",
"log4js": "^6.4.1",
"mime": "^2.5.2",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.5",
"qjobs": "^1.2.0",
"range-parser": "^1.2.1",
"rimraf": "^3.0.2",
"socket.io": "^2.3.0",
"socket.io": "^4.2.0",
"source-map": "^0.6.1",
"tmp": "0.2.1",
"ua-parser-js": "0.7.22",
"yargs": "^15.3.1"
"tmp": "^0.2.1",
"ua-parser-js": "^0.7.30",
"yargs": "^16.1.1"
},
"devDependencies": {
"@commitlint/cli": "^8.3.4",
"@commitlint/config-conventional": "^8.3.4",
"@commitlint/travis-cli": "^8.3.5",
"@semantic-release/changelog": "^3.0.6",
"@semantic-release/git": "^7.0.18",
"@commitlint/cli": "^12.1.4",
"@commitlint/config-angular": "^12.1.4",
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/git": "^9.0.1",
"browserify": "^16.2.3",

@@ -472,16 +494,5 @@ "chai": "^4.2.0",

"eslint-plugin-standard": "^4.0.1",
"grunt": "^1.2.1",
"grunt-auto-release": "^0.0.7",
"grunt-browserify": "^5.0.0",
"grunt-bump": "^0.8.0",
"grunt-check-clean": "^0.1.2",
"grunt-cli": "^1.1.0",
"grunt-contrib-watch": "^1.1.0",
"grunt-conventional-changelog": "^6.0.1",
"grunt-conventional-github-releaser": "^1.0.0",
"grunt-mocha-test": "^0.13.2",
"grunt-npm": "0.0.2",
"http2": "^3.3.6",
"husky": "^4.2.5",
"jasmine-core": "^3.6.0",
"karma": ".",
"karma-browserify": "^7.0.0",

@@ -497,4 +508,2 @@ "karma-browserstack-launcher": "^1.6.0",

"karma-script-launcher": "^1.0.0",
"load-grunt-tasks": "^4.0.0",
"mkdirp": "^0.5.0",
"mocha": "^4.1.0",

@@ -504,3 +513,3 @@ "mocks": "^0.0.15",

"puppeteer": "^1.20.0",
"semantic-release": "^15.14.0",
"semantic-release": "^17.4.7",
"sinon": "7.3.2",

@@ -510,2 +519,3 @@ "sinon-chai": "^3.5.0",

"timer-shim": "^0.3.0",
"watchify": "^3.11.1",
"which": "^1.3.1"

@@ -520,27 +530,19 @@ },

},
"version": "5.2.3",
"version": "6.3.16",
"license": "MIT",
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
"pre-commit": "npm run lint"
}
},
"scripts": {
"lint": "eslint . --ext js --ignore-pattern *.tpl.js",
"lint:fix": "eslint . --ext js --ignore-pattern *.tpl.js --fix",
"test:unit": "grunt test:unit",
"commit:check": "commitlint --from HEAD~1",
"test:unit": "mocha \"test/unit/**/*.spec.js\"",
"test:e2e": "cucumber-js test/e2e/*.feature",
"test:client": "grunt test:client",
"test:client": "node bin/karma start test/client/karma.conf.js",
"test": "npm run test:unit && npm run test:e2e && npm run test:client",
"build": "node scripts/client.js build",
"build:check": "node scripts/client.js check",
"test:appveyor": "grunt test-appveyor",
"build:watch": "node scripts/client.js watch",
"test:integration": "./scripts/integration-tests.sh",
"link": "node --eval \"path=require('path'); require('fs').symlinkSync(path.resolve(__dirname), path.resolve(__dirname, 'node_modules', 'karma'), 'junction')\"",
"unlink": "node --eval \"require('fs').unlinkSync(require('path').resolve(__dirname, 'node_modules', 'karma'))\"",
"init": "rm -rf node_modules/karma && cd node_modules && ln -nsf ../ karma && cd ../",
"init:windows": "(IF EXIST node_modules\\karma (rmdir node_modules\\karma /S /q)) && npm run link",
"semantic-release": "semantic-release"
"semantic-release": "semantic-release",
"commitlint": "commitlint"
}
}
# Karma
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/karma-runner/karma) [![npm version](https://img.shields.io/npm/v/karma.svg?style=flat-square)](https://www.npmjs.com/package/karma) [![npm downloads](https://img.shields.io/npm/dm/karma.svg?style=flat-square)](https://npmcharts.com/compare/karma?minimal=true)
[![Build Status](https://img.shields.io/travis/karma-runner/karma/master.svg?style=flat-square)](https://travis-ci.org/karma-runner/karma) [![Build Status](https://img.shields.io/appveyor/ci/dignifiedquire/karma/master.svg?style=flat-square)](https://ci.appveyor.com/project/dignifiedquire/karma) [![Code Climate](https://img.shields.io/codeclimate/github/karma-runner/karma.svg?style=flat-square)](https://codeclimate.com/github/karma-runner/karma) [![PRs Welcome](https://img.shields.io/badge/prs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![Dependency Status](https://img.shields.io/david/karma-runner/karma.svg?style=flat-square)](https://david-dm.org/karma-runner/karma) [![devDependency Status](https://img.shields.io/david/dev/karma-runner/karma.svg?style=flat-square)](https://david-dm.org/karma-runner/karma#info=devDependencies) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
[![Code Climate](https://img.shields.io/codeclimate/github/karma-runner/karma.svg?style=flat-square)](https://codeclimate.com/github/karma-runner/karma) [![PRs Welcome](https://img.shields.io/badge/prs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com/) [![Dependency Status](https://img.shields.io/david/karma-runner/karma.svg?style=flat-square)](https://david-dm.org/karma-runner/karma) [![devDependency Status](https://img.shields.io/david/dev/karma-runner/karma.svg?style=flat-square)](https://david-dm.org/karma-runner/karma#info=devDependencies) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

@@ -51,3 +51,3 @@ A simple tool that allows you to execute JavaScript code in multiple

* [QUnit]
* and [many others](https://www.npmjs.org/browse/keyword/karma-adapter)
* and [many others](https://www.npmjs.com/search?q=keywords:karma-adapter)

@@ -112,4 +112,4 @@ If you can't find an adapter for your favourite framework, don't worry and write your own.

[JSTD]: https://code.google.com/p/js-test-driver/
[Socket.io]: http://socket.io/
[Node.js]: http://nodejs.org/
[Socket.io]: https://socket.io/
[Node.js]: https://nodejs.org/
[Jasmine]: https://github.com/karma-runner/karma-jasmine

@@ -122,3 +122,3 @@ [Mocha]: https://github.com/karma-runner/karma-mocha

[@JsKarma]: https://twitter.com/JsKarma
[RequireJS]: http://requirejs.org/
[RequireJS]: https://requirejs.org/
[Istanbul]: https://github.com/gotwarlost/istanbul

@@ -125,0 +125,0 @@

module.exports = {
// Add logging for releases until we are fully confident of the release solution.
debug: true,
branch: 'master',
branches: 'master',
verifyConditions: [

@@ -23,3 +23,23 @@ '@semantic-release/changelog',

'./tools/update-docs'
]
],
// The release rules determine what kind of release should be triggered
// based on the information included in the commit message. The default
// rules used by semantic-release are the same, but they are set explicitly
// for better visibility.
// See https://github.com/semantic-release/commit-analyzer/blob/master/lib/default-release-rules.js
releaseRules: [
{ breaking: true, release: 'major' },
{ revert: true, release: 'patch' },
{ type: 'feat', release: 'minor' },
{ type: 'fix', release: 'patch' },
{ type: 'perf', release: 'patch' }
],
// The preset determines which commits are included in the changelog and how
// the changelog is formatted. The default value used by semantic-release is
// the same, but it is set explicitly for visibility.
// See https://semantic-release.gitbook.io/semantic-release/#commit-message-format
// See https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular
preset: 'angular'
}
const browserify = require('browserify')
const fs = require('fs')
const watchify = require('watchify')
const { createWriteStream } = require('fs')
const { readFile } = require('fs').promises

@@ -8,5 +9,5 @@

browserify(inPath).bundle()
.pipe(fs.createWriteStream(outPath))
.once('error', (e) => reject(e))
.pipe(createWriteStream(outPath))
.once('finish', () => resolve())
.once('error', (e) => reject(e))
})

@@ -28,2 +29,24 @@ }

const watchResourceToFile = (inPath, outPath) => {
const b = browserify({
entries: [inPath],
cache: {},
packageCache: {},
plugin: [watchify]
})
const bundle = () => {
b.bundle()
.once('error', (e) => {
console.error(`Failed to bundle ${inPath} into ${outPath}.`)
console.error(e)
})
.pipe(createWriteStream(outPath))
.once('finish', () => console.log(`Bundled ${inPath} into ${outPath}.`))
}
b.on('update', bundle)
bundle()
}
const main = async () => {

@@ -44,2 +67,5 @@ if (process.argv[2] === 'build') {

}
} else if (process.argv[2] === 'watch') {
watchResourceToFile('client/main.js', 'static/karma.js')
watchResourceToFile('context/main.js', 'static/context.js')
} else {

@@ -46,0 +72,0 @@ // eslint-disable-next-line no-throw-literal

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display