123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- (function(window) {
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "(createSpecFilter|createStartFn)" }] */
- 'use strict'
- // Save link to native Date object
- // before it might be mocked by the user
- var _Date = Date
- /**
- * Decision maker for whether a stack entry is considered external to jasmine and karma.
- * @param {String} entry Error stack entry.
- * @return {Boolean} True if external, False otherwise.
- */
- function isExternalStackEntry (entry) {
- return !!entry &&
- // entries related to jasmine and karma-jasmine:
- !/\/(jasmine-core|karma-jasmine)\//.test(entry) &&
- // karma specifics, e.g. "at http://localhost:7018/karma.js:185"
- !/\/(karma.js|context.html):/.test(entry)
- }
- /**
- * Returns relevant stack entries.
- * @param {Array} stack frames
- * @return {Array} A list of relevant stack entries.
- */
- function getRelevantStackFrom (stack) {
- var filteredStack = []
- var relevantStack = []
- for (var i = 0; i < stack.length; i += 1) {
- if (isExternalStackEntry(stack[i])) {
- filteredStack.push(stack[i])
- }
- }
- // If the filtered stack is empty, i.e. the error originated entirely from within jasmine or karma, then the whole stack
- // should be relevant.
- if (filteredStack.length === 0) {
- filteredStack = stack
- }
- for (i = 0; i < filteredStack.length; i += 1) {
- if (filteredStack[i]) {
- relevantStack.push(filteredStack[i])
- }
- }
- return relevantStack
- }
- /**
- * Custom formatter for a failed step.
- *
- * Different browsers report stack trace in different ways. This function
- * attempts to provide a concise, relevant error message by removing the
- * unnecessary stack traces coming from the testing framework itself as well
- * as possible repetition.
- *
- * @see https://github.com/karma-runner/karma-jasmine/issues/60
- * @param {Object} step Step object with stack and message properties.
- * @return {String} Formatted step.
- */
- function formatFailedStep (step) {
- var relevantMessage = []
- var relevantStack = []
- // Safari/Firefox seems to have no stack trace,
- // so we just return the error message and if available
- // construct a stacktrace out of filename and lineno:
- if (!step.stack) {
- if (step.filename) {
- var stackframe = step.filename
- if (step.lineno) {
- stackframe = stackframe + ':' + step.lineno
- }
- relevantStack.push(stackframe)
- }
- relevantMessage.push(step.message)
- return relevantMessage.concat(relevantStack).join('\n')
- }
- // Remove the message prior to processing the stack to prevent issues like
- // https://github.com/karma-runner/karma-jasmine/issues/79
- var stackframes = step.stack.split('\n')
- var messageOnStack = null
- if (stackframes[0].indexOf(step.message) !== -1) {
- // Remove the message if it is in the stack string (eg Chrome)
- messageOnStack = stackframes.shift()
- }
- // Filter frames
- var relevantStackFrames = getRelevantStackFrom(stackframes)
- if (messageOnStack) {
- // Put the message back if we removed it.
- relevantStackFrames.unshift(messageOnStack)
- } else {
- // The stack did not have the step.message so add it.
- relevantStackFrames.unshift(step.message)
- }
- return relevantStackFrames.join('\n')
- }
- function debugUrl (description) {
- // A link to re-run just one failed test case.
- return window.location.origin + '/debug.html?spec=' + encodeURIComponent(description)
- }
- function SuiteNode (name, parent) {
- this.name = name
- this.parent = parent
- this.children = []
- this.addChild = function (name) {
- var suite = new SuiteNode(name, this)
- this.children.push(suite)
- return suite
- }
- }
- function processSuite (suite, pointer) {
- var child
- var childPointer
- for (var i = 0; i < suite.children.length; i++) {
- child = suite.children[i]
- if (child.children) {
- childPointer = pointer[child.description] = { _: [] }
- processSuite(child, childPointer)
- } else {
- if (!pointer._) {
- pointer._ = []
- }
- pointer._.push(child.description)
- }
- }
- }
- function getAllSpecNames (topSuite) {
- var specNames = {}
- processSuite(topSuite, specNames)
- return specNames
- }
- /**
- * Very simple reporter for Jasmine.
- */
- function KarmaReporter (tc, jasmineEnv) {
- var currentSuite = new SuiteNode()
- var startTimeCurrentSpec = new _Date().getTime()
- function handleGlobalErrors (result) {
- if (result.failedExpectations && result.failedExpectations.length) {
- var message = 'An error was thrown in afterAll'
- var steps = result.failedExpectations
- for (var i = 0, l = steps.length; i < l; i++) {
- message += '\n' + formatFailedStep(steps[i])
- }
- tc.error(message)
- }
- }
- /**
- * Jasmine 2.0 dispatches the following events:
- *
- * - jasmineStarted
- * - jasmineDone
- * - suiteStarted
- * - suiteDone
- * - specStarted
- * - specDone
- */
- this.jasmineStarted = function (data) {
- // TODO(vojta): Do not send spec names when polling.
- tc.info({
- event: 'jasmineStarted',
- total: data.totalSpecsDefined,
- specs: getAllSpecNames(jasmineEnv.topSuite())
- })
- }
- this.jasmineDone = function (result) {
- result = result || {}
- // Any errors in top-level afterAll blocks are given here.
- handleGlobalErrors(result)
- // Remove functions from called back results to avoid IPC errors in Electron
- // https://github.com/twolfson/karma-electron/issues/47
- var cleanedOrder
- if (result.order) {
- cleanedOrder = {}
- var orderKeys = Object.getOwnPropertyNames(result.order)
- for (var i = 0; i < orderKeys.length; i++) {
- var orderKey = orderKeys[i]
- if (typeof result.order[orderKey] !== 'function') {
- cleanedOrder[orderKey] = result.order[orderKey]
- }
- }
- }
- tc.complete({
- order: cleanedOrder,
- coverage: window.__coverage__
- })
- }
- this.suiteStarted = function (result) {
- currentSuite = currentSuite.addChild(result.description)
- tc.info({
- event: 'suiteStarted',
- result: result
- })
- }
- this.suiteDone = function (result) {
- // In the case of xdescribe, only "suiteDone" is fired.
- // We need to skip that.
- if (result.description !== currentSuite.name) {
- return
- }
- // Any errors in afterAll blocks are given here, except for top-level
- // afterAll blocks.
- handleGlobalErrors(result)
- currentSuite = currentSuite.parent
- tc.info({
- event: 'suiteDone',
- result: result
- })
- }
- this.specStarted = function () {
- startTimeCurrentSpec = new _Date().getTime()
- }
- this.specDone = function (specResult) {
- var skipped = specResult.status === 'disabled' || specResult.status === 'pending' || specResult.status === 'excluded'
- var result = {
- fullName: specResult.fullName,
- description: specResult.description,
- id: specResult.id,
- log: [],
- skipped: skipped,
- disabled: specResult.status === 'disabled' || specResult.status === 'excluded',
- pending: specResult.status === 'pending',
- success: specResult.failedExpectations.length === 0,
- suite: [],
- time: skipped ? 0 : new _Date().getTime() - startTimeCurrentSpec,
- executedExpectationsCount: specResult.failedExpectations.length + specResult.passedExpectations.length,
- passedExpectations: specResult.passedExpectations,
- properties: specResult.properties
- }
- // generate ordered list of (nested) suite names
- var suitePointer = currentSuite
- while (suitePointer.parent) {
- result.suite.unshift(suitePointer.name)
- suitePointer = suitePointer.parent
- }
- if (!result.success) {
- var steps = specResult.failedExpectations
- for (var i = 0, l = steps.length; i < l; i++) {
- result.log.push(formatFailedStep(steps[i]))
- }
- if (typeof window !== 'undefined' && window.location && window.location.origin) {
- // Report the name of fhe failing spec so the reporter can emit a debug url.
- result.debug_url = debugUrl(specResult.fullName)
- }
- }
- // When failSpecWithNoExpectations is true, Jasmine will report specs without expectations as failed
- if (result.executedExpectationsCount === 0 && specResult.status === 'failed') {
- result.success = false
- result.log.push('Spec has no expectations')
- }
- tc.result(result)
- delete specResult.startTime
- }
- }
- /**
- * Extract grep option from karma config
- * @param {[Array|string]} clientArguments The karma client arguments
- * @return {string} The value of grep option by default empty string
- */
- var getGrepOption = function (clientArguments) {
- var grepRegex = /^--grep=(.*)$/
- if (Object.prototype.toString.call(clientArguments) === '[object Array]') {
- var indexOfGrep = indexOf(clientArguments, '--grep')
- if (indexOfGrep !== -1) {
- return clientArguments[indexOfGrep + 1]
- }
- return map(filter(clientArguments, function (arg) {
- return grepRegex.test(arg)
- }), function (arg) {
- return arg.replace(grepRegex, '$1')
- })[0] || ''
- } else if (typeof clientArguments === 'string') {
- var match = /--grep=([^=]+)/.exec(clientArguments)
- return match ? match[1] : ''
- }
- }
- var createRegExp = function (filter) {
- filter = filter || ''
- if (filter === '') {
- return new RegExp() // to match all
- }
- var regExp = /^[/](.*)[/]([gmixXsuUAJD]*)$/ // pattern to check whether the string is RegExp pattern
- var parts = regExp.exec(filter)
- if (parts === null) {
- return new RegExp(filter.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')) // escape functional symbols
- }
- var patternExpression = parts[1]
- var patternSwitches = parts[2]
- return new RegExp(patternExpression, patternSwitches)
- }
- function getGrepSpecsToRun (clientConfig, specs) {
- var grepOption = getGrepOption(clientConfig.args)
- if (grepOption) {
- var regExp = createRegExp(grepOption)
- return filter(specs, function specFilter (spec) {
- return regExp.test(spec.getFullName())
- })
- }
- }
- function parseQueryParams (location) {
- var params = {}
- if (location && Object.prototype.hasOwnProperty.call(location, 'search')) {
- var pairs = location.search.slice(1).split('&')
- for (var i = 0; i < pairs.length; i++) {
- var keyValue = pairs[i].split('=')
- params[decodeURIComponent(keyValue[0])] =
- decodeURIComponent(keyValue[1])
- }
- }
- return params
- }
- function getId (s) {
- return s.id
- }
- function getSpecsByName (specs, name) {
- specs = specs.filter(function (s) {
- return s.name.indexOf(name) !== -1
- })
- if (specs.length === 0) {
- throw new Error('No spec found with name: "' + name + '"')
- }
- return specs
- }
- function getDebugSpecToRun (location, specs) {
- var queryParams = parseQueryParams(location)
- var spec = queryParams.spec
- if (spec) {
- // A single spec has been requested by name for debugging.
- return getSpecsByName(specs, spec)
- }
- }
- function getSpecsToRunForCurrentShard (specs, shardIndex, totalShards) {
- if (specs.length < totalShards) {
- throw new Error(
- 'More shards (' + totalShards + ') than test specs (' + specs.length +
- ')')
- }
- // Just do a simple sharding strategy of dividing the number of specs
- // equally.
- var firstSpec = Math.floor(specs.length * shardIndex / totalShards)
- var lastSpec = Math.floor(specs.length * (shardIndex + 1) / totalShards)
- return specs.slice(firstSpec, lastSpec)
- }
- function getShardedSpecsToRun (specs, clientConfig) {
- var shardIndex = clientConfig.shardIndex
- var totalShards = clientConfig.totalShards
- if (shardIndex != null && totalShards != null) {
- // Sharded mode - Run only the subset of the specs corresponding to the
- // current shard.
- return getSpecsToRunForCurrentShard(
- specs, Number(shardIndex), Number(totalShards))
- }
- }
- /**
- * Create jasmine spec filter
- * @param {Object} clientConfig karma config
- * @param {!Object} jasmineEnv
- */
- var KarmaSpecFilter = function (clientConfig, jasmineEnv) {
- /**
- * Walk the test suite tree depth first and collect all test specs
- * @param {!Object} jasmineEnv
- * @return {!Array<string>} All possible tests.
- */
- function getAllSpecs (jasmineEnv) {
- var specs = []
- var stack = [jasmineEnv.topSuite()]
- var currentNode
- while ((currentNode = stack.pop())) {
- if (currentNode.children) {
- // jasmine.Suite
- stack = stack.concat(currentNode.children)
- } else if (currentNode.id) {
- // jasmine.Spec
- specs.unshift(currentNode)
- }
- }
- return specs
- }
- /**
- * Filter the specs with URL search params and config.
- * @param {!Object} location property 'search' from URL.
- * @param {!Object} clientConfig karma client config
- * @param {!Object} jasmineEnv
- * @return {!Array<string>}
- */
- function getSpecsToRun (location, clientConfig, jasmineEnv) {
- var specs = getAllSpecs(jasmineEnv).map(function (spec) {
- spec.name = spec.getFullName()
- return spec
- })
- if (!specs || !specs.length) {
- return []
- }
- return getGrepSpecsToRun(clientConfig, specs) ||
- getDebugSpecToRun(location, specs) ||
- getShardedSpecsToRun(specs, clientConfig) ||
- specs
- }
- this.specIdsToRun = new Set(getSpecsToRun(window.location, clientConfig, jasmineEnv).map(getId))
- this.matches = function (spec) {
- return this.specIdsToRun.has(spec.id)
- }
- }
- /**
- * Configure jasmine specFilter
- *
- * This function is invoked from the wrapper.
- * @see adapter.wrapper
- *
- * @param {Object} config The karma config
- * @param {Object} jasmineEnv jasmine environment object
- */
- var createSpecFilter = function (config, jasmineEnv) {
- var karmaSpecFilter = new KarmaSpecFilter(config, jasmineEnv)
- var originalSpecFilter = jasmineEnv.configuration().specFilter
- var specFilter = function (spec) {
- return originalSpecFilter(spec) && karmaSpecFilter.matches(spec)
- }
- return specFilter
- }
- /**
- * Karma starter function factory.
- *
- * This function is invoked from the wrapper.
- * @see adapter.wrapper
- *
- * @param {Object} karma Karma runner instance.
- * @param {Object} [jasmineEnv] Optional Jasmine environment for testing.
- * @return {Function} Karma starter function.
- */
- function createStartFn (karma, jasmineEnv) {
- // This function will be assigned to `window.__karma__.start`:
- return function () {
- var clientConfig = karma.config || {}
- var jasmineConfig = clientConfig.jasmine || {}
- jasmineEnv = jasmineEnv || window.jasmine.getEnv()
- jasmineConfig.specFilter = createSpecFilter(clientConfig, jasmineEnv)
- jasmineEnv.configure(jasmineConfig)
- window.jasmine.DEFAULT_TIMEOUT_INTERVAL = jasmineConfig.timeoutInterval ||
- window.jasmine.DEFAULT_TIMEOUT_INTERVAL
- jasmineEnv.addReporter(new KarmaReporter(karma, jasmineEnv))
- jasmineEnv.execute()
- }
- }
- function indexOf (collection, find, i /* opt */) {
- if (collection.indexOf) {
- return collection.indexOf(find, i)
- }
- if (i === undefined) { i = 0 }
- if (i < 0) { i += collection.length }
- if (i < 0) { i = 0 }
- for (var n = collection.length; i < n; i++) {
- if (i in collection && collection[i] === find) {
- return i
- }
- }
- return -1
- }
- function filter (collection, filter, that /* opt */) {
- if (collection.filter) {
- return collection.filter(filter, that)
- }
- var other = []
- var v
- for (var i = 0, n = collection.length; i < n; i++) {
- if (i in collection && filter.call(that, v = collection[i], i, collection)) {
- other.push(v)
- }
- }
- return other
- }
- function map (collection, mapper, that /* opt */) {
- if (collection.map) {
- return collection.map(mapper, that)
- }
- var other = new Array(collection.length)
- for (var i = 0, n = collection.length; i < n; i++) {
- if (i in collection) {
- other[i] = mapper.call(that, collection[i], i, collection)
- }
- }
- return other
- }
- window.__karma__.start = createStartFn(window.__karma__)
- })(typeof window !== 'undefined' ? window : global);
|