1 | (function(window) {
|
---|
2 |
|
---|
3 | /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "(createSpecFilter|createStartFn)" }] */
|
---|
4 |
|
---|
5 | 'use strict'
|
---|
6 |
|
---|
7 | // Save link to native Date object
|
---|
8 | // before it might be mocked by the user
|
---|
9 | var _Date = Date
|
---|
10 |
|
---|
11 | /**
|
---|
12 | * Decision maker for whether a stack entry is considered external to jasmine and karma.
|
---|
13 | * @param {String} entry Error stack entry.
|
---|
14 | * @return {Boolean} True if external, False otherwise.
|
---|
15 | */
|
---|
16 | function isExternalStackEntry (entry) {
|
---|
17 | return !!entry &&
|
---|
18 | // entries related to jasmine and karma-jasmine:
|
---|
19 | !/\/(jasmine-core|karma-jasmine)\//.test(entry) &&
|
---|
20 | // karma specifics, e.g. "at http://localhost:7018/karma.js:185"
|
---|
21 | !/\/(karma.js|context.html):/.test(entry)
|
---|
22 | }
|
---|
23 |
|
---|
24 | /**
|
---|
25 | * Returns relevant stack entries.
|
---|
26 | * @param {Array} stack frames
|
---|
27 | * @return {Array} A list of relevant stack entries.
|
---|
28 | */
|
---|
29 | function getRelevantStackFrom (stack) {
|
---|
30 | var filteredStack = []
|
---|
31 | var relevantStack = []
|
---|
32 |
|
---|
33 | for (var i = 0; i < stack.length; i += 1) {
|
---|
34 | if (isExternalStackEntry(stack[i])) {
|
---|
35 | filteredStack.push(stack[i])
|
---|
36 | }
|
---|
37 | }
|
---|
38 |
|
---|
39 | // If the filtered stack is empty, i.e. the error originated entirely from within jasmine or karma, then the whole stack
|
---|
40 | // should be relevant.
|
---|
41 | if (filteredStack.length === 0) {
|
---|
42 | filteredStack = stack
|
---|
43 | }
|
---|
44 |
|
---|
45 | for (i = 0; i < filteredStack.length; i += 1) {
|
---|
46 | if (filteredStack[i]) {
|
---|
47 | relevantStack.push(filteredStack[i])
|
---|
48 | }
|
---|
49 | }
|
---|
50 |
|
---|
51 | return relevantStack
|
---|
52 | }
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * Custom formatter for a failed step.
|
---|
56 | *
|
---|
57 | * Different browsers report stack trace in different ways. This function
|
---|
58 | * attempts to provide a concise, relevant error message by removing the
|
---|
59 | * unnecessary stack traces coming from the testing framework itself as well
|
---|
60 | * as possible repetition.
|
---|
61 | *
|
---|
62 | * @see https://github.com/karma-runner/karma-jasmine/issues/60
|
---|
63 | * @param {Object} step Step object with stack and message properties.
|
---|
64 | * @return {String} Formatted step.
|
---|
65 | */
|
---|
66 | function formatFailedStep (step) {
|
---|
67 | var relevantMessage = []
|
---|
68 | var relevantStack = []
|
---|
69 |
|
---|
70 | // Safari/Firefox seems to have no stack trace,
|
---|
71 | // so we just return the error message and if available
|
---|
72 | // construct a stacktrace out of filename and lineno:
|
---|
73 | if (!step.stack) {
|
---|
74 | if (step.filename) {
|
---|
75 | var stackframe = step.filename
|
---|
76 | if (step.lineno) {
|
---|
77 | stackframe = stackframe + ':' + step.lineno
|
---|
78 | }
|
---|
79 | relevantStack.push(stackframe)
|
---|
80 | }
|
---|
81 | relevantMessage.push(step.message)
|
---|
82 | return relevantMessage.concat(relevantStack).join('\n')
|
---|
83 | }
|
---|
84 |
|
---|
85 | // Remove the message prior to processing the stack to prevent issues like
|
---|
86 | // https://github.com/karma-runner/karma-jasmine/issues/79
|
---|
87 | var stackframes = step.stack.split('\n')
|
---|
88 | var messageOnStack = null
|
---|
89 | if (stackframes[0].indexOf(step.message) !== -1) {
|
---|
90 | // Remove the message if it is in the stack string (eg Chrome)
|
---|
91 | messageOnStack = stackframes.shift()
|
---|
92 | }
|
---|
93 | // Filter frames
|
---|
94 | var relevantStackFrames = getRelevantStackFrom(stackframes)
|
---|
95 | if (messageOnStack) {
|
---|
96 | // Put the message back if we removed it.
|
---|
97 | relevantStackFrames.unshift(messageOnStack)
|
---|
98 | } else {
|
---|
99 | // The stack did not have the step.message so add it.
|
---|
100 | relevantStackFrames.unshift(step.message)
|
---|
101 | }
|
---|
102 |
|
---|
103 | return relevantStackFrames.join('\n')
|
---|
104 | }
|
---|
105 |
|
---|
106 | function debugUrl (description) {
|
---|
107 | // A link to re-run just one failed test case.
|
---|
108 | return window.location.origin + '/debug.html?spec=' + encodeURIComponent(description)
|
---|
109 | }
|
---|
110 |
|
---|
111 | function SuiteNode (name, parent) {
|
---|
112 | this.name = name
|
---|
113 | this.parent = parent
|
---|
114 | this.children = []
|
---|
115 |
|
---|
116 | this.addChild = function (name) {
|
---|
117 | var suite = new SuiteNode(name, this)
|
---|
118 | this.children.push(suite)
|
---|
119 | return suite
|
---|
120 | }
|
---|
121 | }
|
---|
122 |
|
---|
123 | function processSuite (suite, pointer) {
|
---|
124 | var child
|
---|
125 | var childPointer
|
---|
126 |
|
---|
127 | for (var i = 0; i < suite.children.length; i++) {
|
---|
128 | child = suite.children[i]
|
---|
129 |
|
---|
130 | if (child.children) {
|
---|
131 | childPointer = pointer[child.description] = { _: [] }
|
---|
132 | processSuite(child, childPointer)
|
---|
133 | } else {
|
---|
134 | if (!pointer._) {
|
---|
135 | pointer._ = []
|
---|
136 | }
|
---|
137 | pointer._.push(child.description)
|
---|
138 | }
|
---|
139 | }
|
---|
140 | }
|
---|
141 |
|
---|
142 | function getAllSpecNames (topSuite) {
|
---|
143 | var specNames = {}
|
---|
144 |
|
---|
145 | processSuite(topSuite, specNames)
|
---|
146 |
|
---|
147 | return specNames
|
---|
148 | }
|
---|
149 |
|
---|
150 | /**
|
---|
151 | * Very simple reporter for Jasmine.
|
---|
152 | */
|
---|
153 | function KarmaReporter (tc, jasmineEnv) {
|
---|
154 | var currentSuite = new SuiteNode()
|
---|
155 |
|
---|
156 | var startTimeCurrentSpec = new _Date().getTime()
|
---|
157 |
|
---|
158 | function handleGlobalErrors (result) {
|
---|
159 | if (result.failedExpectations && result.failedExpectations.length) {
|
---|
160 | var message = 'An error was thrown in afterAll'
|
---|
161 | var steps = result.failedExpectations
|
---|
162 | for (var i = 0, l = steps.length; i < l; i++) {
|
---|
163 | message += '\n' + formatFailedStep(steps[i])
|
---|
164 | }
|
---|
165 |
|
---|
166 | tc.error(message)
|
---|
167 | }
|
---|
168 | }
|
---|
169 |
|
---|
170 | /**
|
---|
171 | * Jasmine 2.0 dispatches the following events:
|
---|
172 | *
|
---|
173 | * - jasmineStarted
|
---|
174 | * - jasmineDone
|
---|
175 | * - suiteStarted
|
---|
176 | * - suiteDone
|
---|
177 | * - specStarted
|
---|
178 | * - specDone
|
---|
179 | */
|
---|
180 |
|
---|
181 | this.jasmineStarted = function (data) {
|
---|
182 | // TODO(vojta): Do not send spec names when polling.
|
---|
183 | tc.info({
|
---|
184 | event: 'jasmineStarted',
|
---|
185 | total: data.totalSpecsDefined,
|
---|
186 | specs: getAllSpecNames(jasmineEnv.topSuite())
|
---|
187 | })
|
---|
188 | }
|
---|
189 |
|
---|
190 | this.jasmineDone = function (result) {
|
---|
191 | result = result || {}
|
---|
192 |
|
---|
193 | // Any errors in top-level afterAll blocks are given here.
|
---|
194 | handleGlobalErrors(result)
|
---|
195 |
|
---|
196 | // Remove functions from called back results to avoid IPC errors in Electron
|
---|
197 | // https://github.com/twolfson/karma-electron/issues/47
|
---|
198 | var cleanedOrder
|
---|
199 | if (result.order) {
|
---|
200 | cleanedOrder = {}
|
---|
201 | var orderKeys = Object.getOwnPropertyNames(result.order)
|
---|
202 | for (var i = 0; i < orderKeys.length; i++) {
|
---|
203 | var orderKey = orderKeys[i]
|
---|
204 | if (typeof result.order[orderKey] !== 'function') {
|
---|
205 | cleanedOrder[orderKey] = result.order[orderKey]
|
---|
206 | }
|
---|
207 | }
|
---|
208 | }
|
---|
209 |
|
---|
210 | tc.complete({
|
---|
211 | order: cleanedOrder,
|
---|
212 | coverage: window.__coverage__
|
---|
213 | })
|
---|
214 | }
|
---|
215 |
|
---|
216 | this.suiteStarted = function (result) {
|
---|
217 | currentSuite = currentSuite.addChild(result.description)
|
---|
218 | tc.info({
|
---|
219 | event: 'suiteStarted',
|
---|
220 | result: result
|
---|
221 | })
|
---|
222 | }
|
---|
223 |
|
---|
224 | this.suiteDone = function (result) {
|
---|
225 | // In the case of xdescribe, only "suiteDone" is fired.
|
---|
226 | // We need to skip that.
|
---|
227 | if (result.description !== currentSuite.name) {
|
---|
228 | return
|
---|
229 | }
|
---|
230 |
|
---|
231 | // Any errors in afterAll blocks are given here, except for top-level
|
---|
232 | // afterAll blocks.
|
---|
233 | handleGlobalErrors(result)
|
---|
234 |
|
---|
235 | currentSuite = currentSuite.parent
|
---|
236 |
|
---|
237 | tc.info({
|
---|
238 | event: 'suiteDone',
|
---|
239 | result: result
|
---|
240 | })
|
---|
241 | }
|
---|
242 |
|
---|
243 | this.specStarted = function () {
|
---|
244 | startTimeCurrentSpec = new _Date().getTime()
|
---|
245 | }
|
---|
246 |
|
---|
247 | this.specDone = function (specResult) {
|
---|
248 | var skipped = specResult.status === 'disabled' || specResult.status === 'pending' || specResult.status === 'excluded'
|
---|
249 | var result = {
|
---|
250 | fullName: specResult.fullName,
|
---|
251 | description: specResult.description,
|
---|
252 | id: specResult.id,
|
---|
253 | log: [],
|
---|
254 | skipped: skipped,
|
---|
255 | disabled: specResult.status === 'disabled' || specResult.status === 'excluded',
|
---|
256 | pending: specResult.status === 'pending',
|
---|
257 | success: specResult.failedExpectations.length === 0,
|
---|
258 | suite: [],
|
---|
259 | time: skipped ? 0 : new _Date().getTime() - startTimeCurrentSpec,
|
---|
260 | executedExpectationsCount: specResult.failedExpectations.length + specResult.passedExpectations.length,
|
---|
261 | passedExpectations: specResult.passedExpectations,
|
---|
262 | properties: specResult.properties
|
---|
263 | }
|
---|
264 |
|
---|
265 | // generate ordered list of (nested) suite names
|
---|
266 | var suitePointer = currentSuite
|
---|
267 | while (suitePointer.parent) {
|
---|
268 | result.suite.unshift(suitePointer.name)
|
---|
269 | suitePointer = suitePointer.parent
|
---|
270 | }
|
---|
271 |
|
---|
272 | if (!result.success) {
|
---|
273 | var steps = specResult.failedExpectations
|
---|
274 | for (var i = 0, l = steps.length; i < l; i++) {
|
---|
275 | result.log.push(formatFailedStep(steps[i]))
|
---|
276 | }
|
---|
277 |
|
---|
278 | if (typeof window !== 'undefined' && window.location && window.location.origin) {
|
---|
279 | // Report the name of fhe failing spec so the reporter can emit a debug url.
|
---|
280 | result.debug_url = debugUrl(specResult.fullName)
|
---|
281 | }
|
---|
282 | }
|
---|
283 |
|
---|
284 | // When failSpecWithNoExpectations is true, Jasmine will report specs without expectations as failed
|
---|
285 | if (result.executedExpectationsCount === 0 && specResult.status === 'failed') {
|
---|
286 | result.success = false
|
---|
287 | result.log.push('Spec has no expectations')
|
---|
288 | }
|
---|
289 |
|
---|
290 | tc.result(result)
|
---|
291 | delete specResult.startTime
|
---|
292 | }
|
---|
293 | }
|
---|
294 |
|
---|
295 | /**
|
---|
296 | * Extract grep option from karma config
|
---|
297 | * @param {[Array|string]} clientArguments The karma client arguments
|
---|
298 | * @return {string} The value of grep option by default empty string
|
---|
299 | */
|
---|
300 | var getGrepOption = function (clientArguments) {
|
---|
301 | var grepRegex = /^--grep=(.*)$/
|
---|
302 |
|
---|
303 | if (Object.prototype.toString.call(clientArguments) === '[object Array]') {
|
---|
304 | var indexOfGrep = indexOf(clientArguments, '--grep')
|
---|
305 |
|
---|
306 | if (indexOfGrep !== -1) {
|
---|
307 | return clientArguments[indexOfGrep + 1]
|
---|
308 | }
|
---|
309 |
|
---|
310 | return map(filter(clientArguments, function (arg) {
|
---|
311 | return grepRegex.test(arg)
|
---|
312 | }), function (arg) {
|
---|
313 | return arg.replace(grepRegex, '$1')
|
---|
314 | })[0] || ''
|
---|
315 | } else if (typeof clientArguments === 'string') {
|
---|
316 | var match = /--grep=([^=]+)/.exec(clientArguments)
|
---|
317 |
|
---|
318 | return match ? match[1] : ''
|
---|
319 | }
|
---|
320 | }
|
---|
321 |
|
---|
322 | var createRegExp = function (filter) {
|
---|
323 | filter = filter || ''
|
---|
324 | if (filter === '') {
|
---|
325 | return new RegExp() // to match all
|
---|
326 | }
|
---|
327 |
|
---|
328 | var regExp = /^[/](.*)[/]([gmixXsuUAJD]*)$/ // pattern to check whether the string is RegExp pattern
|
---|
329 |
|
---|
330 | var parts = regExp.exec(filter)
|
---|
331 | if (parts === null) {
|
---|
332 | return new RegExp(filter.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')) // escape functional symbols
|
---|
333 | }
|
---|
334 |
|
---|
335 | var patternExpression = parts[1]
|
---|
336 | var patternSwitches = parts[2]
|
---|
337 | return new RegExp(patternExpression, patternSwitches)
|
---|
338 | }
|
---|
339 |
|
---|
340 | function getGrepSpecsToRun (clientConfig, specs) {
|
---|
341 | var grepOption = getGrepOption(clientConfig.args)
|
---|
342 | if (grepOption) {
|
---|
343 | var regExp = createRegExp(grepOption)
|
---|
344 | return filter(specs, function specFilter (spec) {
|
---|
345 | return regExp.test(spec.getFullName())
|
---|
346 | })
|
---|
347 | }
|
---|
348 | }
|
---|
349 |
|
---|
350 | function parseQueryParams (location) {
|
---|
351 | var params = {}
|
---|
352 | if (location && Object.prototype.hasOwnProperty.call(location, 'search')) {
|
---|
353 | var pairs = location.search.substr(1).split('&')
|
---|
354 | for (var i = 0; i < pairs.length; i++) {
|
---|
355 | var keyValue = pairs[i].split('=')
|
---|
356 | params[decodeURIComponent(keyValue[0])] =
|
---|
357 | decodeURIComponent(keyValue[1])
|
---|
358 | }
|
---|
359 | }
|
---|
360 | return params
|
---|
361 | }
|
---|
362 |
|
---|
363 | function getId (s) {
|
---|
364 | return s.id
|
---|
365 | }
|
---|
366 |
|
---|
367 | function getSpecsByName (specs, name) {
|
---|
368 | specs = specs.filter(function (s) {
|
---|
369 | return s.name.indexOf(name) !== -1
|
---|
370 | })
|
---|
371 | if (specs.length === 0) {
|
---|
372 | throw new Error('No spec found with name: "' + name + '"')
|
---|
373 | }
|
---|
374 | return specs
|
---|
375 | }
|
---|
376 |
|
---|
377 | function getDebugSpecToRun (location, specs) {
|
---|
378 | var queryParams = parseQueryParams(location)
|
---|
379 | var spec = queryParams.spec
|
---|
380 | if (spec) {
|
---|
381 | // A single spec has been requested by name for debugging.
|
---|
382 | return getSpecsByName(specs, spec)
|
---|
383 | }
|
---|
384 | }
|
---|
385 |
|
---|
386 | function getSpecsToRunForCurrentShard (specs, shardIndex, totalShards) {
|
---|
387 | if (specs.length < totalShards) {
|
---|
388 | throw new Error(
|
---|
389 | 'More shards (' + totalShards + ') than test specs (' + specs.length +
|
---|
390 | ')')
|
---|
391 | }
|
---|
392 |
|
---|
393 | // Just do a simple sharding strategy of dividing the number of specs
|
---|
394 | // equally.
|
---|
395 | var firstSpec = Math.floor(specs.length * shardIndex / totalShards)
|
---|
396 | var lastSpec = Math.floor(specs.length * (shardIndex + 1) / totalShards)
|
---|
397 | return specs.slice(firstSpec, lastSpec)
|
---|
398 | }
|
---|
399 |
|
---|
400 | function getShardedSpecsToRun (specs, clientConfig) {
|
---|
401 | var shardIndex = clientConfig.shardIndex
|
---|
402 | var totalShards = clientConfig.totalShards
|
---|
403 | if (shardIndex != null && totalShards != null) {
|
---|
404 | // Sharded mode - Run only the subset of the specs corresponding to the
|
---|
405 | // current shard.
|
---|
406 | return getSpecsToRunForCurrentShard(
|
---|
407 | specs, Number(shardIndex), Number(totalShards))
|
---|
408 | }
|
---|
409 | }
|
---|
410 |
|
---|
411 | /**
|
---|
412 | * Create jasmine spec filter
|
---|
413 | * @param {Object} clientConfig karma config
|
---|
414 | * @param {!Object} jasmineEnv
|
---|
415 | */
|
---|
416 | var KarmaSpecFilter = function (clientConfig, jasmineEnv) {
|
---|
417 | /**
|
---|
418 | * Walk the test suite tree depth first and collect all test specs
|
---|
419 | * @param {!Object} jasmineEnv
|
---|
420 | * @return {!Array<string>} All possible tests.
|
---|
421 | */
|
---|
422 | function getAllSpecs (jasmineEnv) {
|
---|
423 | var specs = []
|
---|
424 | var stack = [jasmineEnv.topSuite()]
|
---|
425 | var currentNode
|
---|
426 | while ((currentNode = stack.pop())) {
|
---|
427 | if (currentNode.children) {
|
---|
428 | // jasmine.Suite
|
---|
429 | stack = stack.concat(currentNode.children)
|
---|
430 | } else if (currentNode.id) {
|
---|
431 | // jasmine.Spec
|
---|
432 | specs.unshift(currentNode)
|
---|
433 | }
|
---|
434 | }
|
---|
435 |
|
---|
436 | return specs
|
---|
437 | }
|
---|
438 |
|
---|
439 | /**
|
---|
440 | * Filter the specs with URL search params and config.
|
---|
441 | * @param {!Object} location property 'search' from URL.
|
---|
442 | * @param {!Object} clientConfig karma client config
|
---|
443 | * @param {!Object} jasmineEnv
|
---|
444 | * @return {!Array<string>}
|
---|
445 | */
|
---|
446 | function getSpecsToRun (location, clientConfig, jasmineEnv) {
|
---|
447 | var specs = getAllSpecs(jasmineEnv).map(function (spec) {
|
---|
448 | spec.name = spec.getFullName()
|
---|
449 | return spec
|
---|
450 | })
|
---|
451 |
|
---|
452 | if (!specs || !specs.length) {
|
---|
453 | return []
|
---|
454 | }
|
---|
455 |
|
---|
456 | return getGrepSpecsToRun(clientConfig, specs) ||
|
---|
457 | getDebugSpecToRun(location, specs) ||
|
---|
458 | getShardedSpecsToRun(specs, clientConfig) ||
|
---|
459 | specs
|
---|
460 | }
|
---|
461 |
|
---|
462 | this.specIdsToRun =
|
---|
463 | getSpecsToRun(window.location, clientConfig, jasmineEnv).map(getId)
|
---|
464 |
|
---|
465 | this.matches = function (spec) {
|
---|
466 | return this.specIdsToRun.indexOf(spec.id) !== -1
|
---|
467 | }
|
---|
468 | }
|
---|
469 |
|
---|
470 | /**
|
---|
471 | * Configure jasmine specFilter
|
---|
472 | *
|
---|
473 | * This function is invoked from the wrapper.
|
---|
474 | * @see adapter.wrapper
|
---|
475 | *
|
---|
476 | * @param {Object} config The karma config
|
---|
477 | * @param {Object} jasmineEnv jasmine environment object
|
---|
478 | */
|
---|
479 | var createSpecFilter = function (config, jasmineEnv) {
|
---|
480 | var karmaSpecFilter = new KarmaSpecFilter(config, jasmineEnv)
|
---|
481 |
|
---|
482 | var specFilter = function (spec) {
|
---|
483 | return karmaSpecFilter.matches(spec)
|
---|
484 | }
|
---|
485 |
|
---|
486 | return specFilter
|
---|
487 | }
|
---|
488 |
|
---|
489 | /**
|
---|
490 | * Karma starter function factory.
|
---|
491 | *
|
---|
492 | * This function is invoked from the wrapper.
|
---|
493 | * @see adapter.wrapper
|
---|
494 | *
|
---|
495 | * @param {Object} karma Karma runner instance.
|
---|
496 | * @param {Object} [jasmineEnv] Optional Jasmine environment for testing.
|
---|
497 | * @return {Function} Karma starter function.
|
---|
498 | */
|
---|
499 | function createStartFn (karma, jasmineEnv) {
|
---|
500 | // This function will be assigned to `window.__karma__.start`:
|
---|
501 | return function () {
|
---|
502 | var clientConfig = karma.config || {}
|
---|
503 | var jasmineConfig = clientConfig.jasmine || {}
|
---|
504 |
|
---|
505 | jasmineEnv = jasmineEnv || window.jasmine.getEnv()
|
---|
506 |
|
---|
507 | jasmineConfig.specFilter = createSpecFilter(clientConfig, jasmineEnv)
|
---|
508 |
|
---|
509 | jasmineEnv.configure(jasmineConfig)
|
---|
510 |
|
---|
511 | window.jasmine.DEFAULT_TIMEOUT_INTERVAL = jasmineConfig.timeoutInterval ||
|
---|
512 | window.jasmine.DEFAULT_TIMEOUT_INTERVAL
|
---|
513 | jasmineEnv.addReporter(new KarmaReporter(karma, jasmineEnv))
|
---|
514 | jasmineEnv.execute()
|
---|
515 | }
|
---|
516 | }
|
---|
517 |
|
---|
518 | function indexOf (collection, find, i /* opt */) {
|
---|
519 | if (collection.indexOf) {
|
---|
520 | return collection.indexOf(find, i)
|
---|
521 | }
|
---|
522 |
|
---|
523 | if (i === undefined) { i = 0 }
|
---|
524 | if (i < 0) { i += collection.length }
|
---|
525 | if (i < 0) { i = 0 }
|
---|
526 | for (var n = collection.length; i < n; i++) {
|
---|
527 | if (i in collection && collection[i] === find) {
|
---|
528 | return i
|
---|
529 | }
|
---|
530 | }
|
---|
531 | return -1
|
---|
532 | }
|
---|
533 |
|
---|
534 | function filter (collection, filter, that /* opt */) {
|
---|
535 | if (collection.filter) {
|
---|
536 | return collection.filter(filter, that)
|
---|
537 | }
|
---|
538 |
|
---|
539 | var other = []
|
---|
540 | var v
|
---|
541 | for (var i = 0, n = collection.length; i < n; i++) {
|
---|
542 | if (i in collection && filter.call(that, v = collection[i], i, collection)) {
|
---|
543 | other.push(v)
|
---|
544 | }
|
---|
545 | }
|
---|
546 | return other
|
---|
547 | }
|
---|
548 |
|
---|
549 | function map (collection, mapper, that /* opt */) {
|
---|
550 | if (collection.map) {
|
---|
551 | return collection.map(mapper, that)
|
---|
552 | }
|
---|
553 |
|
---|
554 | var other = new Array(collection.length)
|
---|
555 | for (var i = 0, n = collection.length; i < n; i++) {
|
---|
556 | if (i in collection) {
|
---|
557 | other[i] = mapper.call(that, collection[i], i, collection)
|
---|
558 | }
|
---|
559 | }
|
---|
560 | return other
|
---|
561 | }
|
---|
562 |
|
---|
563 | window.__karma__.start = createStartFn(window.__karma__)
|
---|
564 |
|
---|
565 | })(typeof window !== 'undefined' ? window : global);
|
---|