karma-parallel
Advanced tools
Comparing version 0.1.1 to 0.1.2
@@ -23,3 +23,3 @@ // Karma configuration | ||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter | ||
frameworks: ['jasmine', 'parallel'], | ||
frameworks: ['parallel', 'jasmine'], | ||
@@ -26,0 +26,0 @@ |
'use strict'; | ||
const path = require('path'); | ||
// jshint node: true | ||
@@ -54,2 +56,9 @@ | ||
module.exports = function(/* config */fullConfig, emitter, logger) { | ||
if (fullConfig.frameworks[0] !== 'parallel') { | ||
// We have to be loaded first to make sure we load our parallelizer script *after* the jasmine/mocha script runs | ||
throw new Error(`The "parallel" framework must be loaded first into the karma frameworks array. \nActual: config.frameworks: ${JSON.stringify(fullConfig.frameworks)}`); | ||
} | ||
fullConfig.files.unshift({pattern: path.join(__dirname, 'karma-parallelizer.js'), included: true, served: true, watched: false}); | ||
const log = logger.create('framework:karma-parallel'); | ||
@@ -56,0 +65,0 @@ const config = getConfig(fullConfig); |
@@ -5,3 +5,3 @@ 'use strict'; | ||
function initKarmaParallelizer(root, shardIndexInfo) { | ||
function initKarmaParallelizer(root, karma, shardIndexInfo) { | ||
if (!shardIndexInfo || !shardIndexInfo.shouldShard) { | ||
@@ -12,40 +12,12 @@ // console.log('Skipping sharding. Could not find index and count values'); | ||
const strategy = getSpecSuiteStrategy(shardIndexInfo); | ||
const overrideSpecSuite = createSpecSuiteOverrider(strategy); | ||
var strategy = getSpecSuiteStrategy(shardIndexInfo); | ||
var fakeContextStatus = createFakeTestContext(root, strategy); | ||
// Mocha uses describe.only|skip | ||
// Jasmine uses fdescribe|ddescribe|xdescribe | ||
replaceMethod(root, 'describe', function(method) { | ||
const overriden = overrideSpecSuite(method); | ||
replaceMethod(overriden, 'only', overrideSpecSuite); | ||
replaceMethod(overriden, 'skip', overrideSpecSuite); | ||
return overriden; | ||
}); | ||
replaceMethod(root, 'xdescribe', overrideSpecSuite); | ||
replaceMethod(root, 'fdescribe', overrideSpecSuite); | ||
replaceMethod(root, 'ddescribe', overrideSpecSuite); | ||
var origStart = karma.start; | ||
karma.start = function() { | ||
fakeContextStatus.beforeStartup(); | ||
origStart.call(this); | ||
}; | ||
} | ||
function replaceMethod(root, methodName, overrider) { | ||
const tmpOriginalFunction = root[methodName]; | ||
let overriddenFunction; | ||
// Make a getter / setter that overrides the method when initialized | ||
Object.defineProperty(root, methodName, { | ||
enumerable: true, | ||
configurable: true, | ||
get: function() { | ||
return overriddenFunction; | ||
}, | ||
set: function(method) { | ||
overriddenFunction = overrider(method); | ||
} | ||
}); | ||
// If we already have a function on the global, immediately set it back which wraps and overrides it | ||
if (typeof tmpOriginalFunction === 'function') { | ||
root[methodName] = tmpOriginalFunction; | ||
} | ||
} | ||
function getSpecSuiteStrategy(shardIndexInfo) { | ||
@@ -62,21 +34,6 @@ switch (shardIndexInfo.shardStrategy) { | ||
function createSpecSuiteOverrider(strategy) { | ||
let depth = 0; | ||
return function overrideSpecSuite(origDescribe) { | ||
return function(description, specDefinitions) { | ||
// If we are a top-level, ask our strategy if we should be interested in this suite | ||
if (depth === 0 && !strategy(description, specDefinitions)) { | ||
// console.log('[skipping]', description); | ||
} else { | ||
origDescribe(description, function() { | ||
depth++; | ||
specDefinitions(); | ||
depth--; | ||
}); | ||
} | ||
}; | ||
}; | ||
} | ||
function createDescriptionLengthStragegy({shardIndex, executors}) { | ||
function createDescriptionLengthStragegy(shardIndexInfo) { | ||
var | ||
shardIndex = shardIndexInfo.shardIndex, | ||
executors = shardIndexInfo.executors; | ||
return function overrideSpecSuite(description/*, specDefinitions*/) { | ||
@@ -87,6 +44,9 @@ return description.length % executors === shardIndex; | ||
function createRoundRobinStrategy({shardIndex, executors}) { | ||
function createRoundRobinStrategy(shardIndexInfo) { | ||
var | ||
shardIndex = shardIndexInfo.shardIndex, | ||
executors = shardIndexInfo.executors; | ||
// Increment the count on each top level describe to determine | ||
// round-robin responsibility | ||
let count = 0; | ||
var count = 0; | ||
return function(/*description, specDefinitions*/) { | ||
@@ -97,2 +57,95 @@ return count++ % executors === shardIndex; | ||
initKarmaParallelizer(window, JSON.parse('%KARMA_SHARD_INFO%')); | ||
function createFakeTestContext(ctx, shouldUseDescription) { | ||
var depth = 0; | ||
var isFaking = false; // Are we currently faking out describe blocks to look for tests for other instances | ||
var hasFocusedWhileFaking = false; // Have we found a focus tests for another instance | ||
var hasFocusedWithoutFaking = false; // Have we registerd at least one focus test for this instance | ||
var hasSpecs = false; // Have we registered at least one test for this instance | ||
var forceDescribe = false; // | ||
function wrapDescription(def) { | ||
return function() { | ||
try { | ||
depth++; | ||
def.call(this); | ||
} finally { | ||
depth--; | ||
} | ||
}; | ||
} | ||
function wrap(fn, isFocus, isDescription, isSpec) { | ||
if (!fn) return fn; | ||
return function(name, def) { | ||
if (isDescription && depth === 0) { | ||
// Reset isFaking on top-level descriptions | ||
isFaking = !shouldUseDescription(name, def); | ||
} | ||
hasSpecs = hasSpecs || (isSpec && !isFaking); | ||
hasFocusedWhileFaking = hasFocusedWhileFaking || (isFocus && isFaking); | ||
hasFocusedWithoutFaking = hasFocusedWithoutFaking || (isFocus && !isFaking); | ||
if (isDescription) def = wrapDescription(def); | ||
if (!isFaking || forceDescribe) { | ||
// Call through to framework | ||
fn.call(this, name, def); | ||
} else if (isDescription) { | ||
// If its a fake description, then we need to call through to eval inner its() looking for focuses | ||
// TODO, do we ever need parameters? | ||
def(); | ||
} | ||
}; | ||
} | ||
// Save as vars before we replace them | ||
var describeOnly = ctx.describe.only; | ||
var describeSkip = ctx.describe.skip; | ||
var itOnly = ctx.it.only; | ||
var itSkip = ctx.it.skip; | ||
ctx.describe = wrap(ctx.describe, false, true, false); | ||
ctx.context = wrap(ctx.context, false, true, false); | ||
ctx.xdescribe = wrap(ctx.xdescribe, false, true, false); | ||
ctx.describe.skip = wrap(describeSkip, false, true, false); | ||
ctx.fdescribe = wrap(ctx.fdescribe, true, true, false); | ||
ctx.ddescribe = wrap(ctx.ddescribe, true, true, false); | ||
ctx.describe.only = wrap(describeOnly, true, true, false); | ||
ctx.it = wrap(ctx.it, false, false, true); | ||
ctx.specify = wrap(ctx.specify, false, false, true); | ||
ctx.xit = wrap(ctx.xit, false, false, true); | ||
ctx.xspecify = wrap(ctx.xspecify, false, false, true); | ||
ctx.it.skip = wrap(itSkip, false, false, true); | ||
ctx.fit = wrap(ctx.fit, true, false, true); | ||
ctx.iit = wrap(ctx.iit, true, false, true); | ||
ctx.it.only = wrap(itOnly, true, false, true); | ||
ctx.before = wrap(ctx.before, false, false, false); | ||
ctx.beforeAll = wrap(ctx.beforeAll, false, false, false); | ||
ctx.beforeEach = wrap(ctx.beforeEach, false, false, false); | ||
ctx.beforeAll = wrap(ctx.beforeAll, false, false, false); | ||
ctx.after = wrap(ctx.after, false, false, false); | ||
ctx.afterEach = wrap(ctx.afterEach, false, false, false); | ||
return { | ||
beforeStartup: function() { | ||
forceDescribe = true; | ||
if (hasFocusedWhileFaking && !hasFocusedWithoutFaking) { | ||
ctx.describe('[karma-parallel] Fake focused test spec', function() { | ||
(ctx.fit || ctx.iit || ctx.it.only).call(ctx, 'should prevent other tests from running', function(){}); | ||
}); | ||
} | ||
if (!hasSpecs) { | ||
ctx.describe('[karma-parallel] Add single test to prevent failure', function() { | ||
(ctx.it || ctx.specify).call(ctx, 'should prevent failing by having sucessful tests', function(){}); | ||
}); | ||
} | ||
forceDescribe = false; | ||
} | ||
}; | ||
} | ||
initKarmaParallelizer(window, window.__karma__, JSON.parse('%KARMA_SHARD_INFO%')); | ||
@@ -9,4 +9,3 @@ 'use strict'; | ||
const karmaParallelScriptName = 'karma-parallelizer.js'; | ||
const karmaParallelScriptPath = path.join(__dirname, karmaParallelScriptName); | ||
const karmaParallelScript = fs.readFileSync(karmaParallelScriptPath, 'utf8'); // eslint-disable-line security/detect-non-literal-fs-filename | ||
const karmaParallelScript = fs.readFileSync(path.join(__dirname, karmaParallelScriptName), 'utf8'); // eslint-disable-line security/detect-non-literal-fs-filename | ||
@@ -41,4 +40,2 @@ const idParamExtractor = /\/\?id=(\d+)/; | ||
module.exports = function(/* config */fullConfig, /* config.parallelOptions */config) { | ||
fullConfig.files.unshift({pattern: karmaParallelScriptPath, included: true, served: true, watched: false}); | ||
return function (request, response, next) { | ||
@@ -45,0 +42,0 @@ // Responsible for finding the id of the browser and saving it as a cookie so all future requests can access it |
@@ -57,3 +57,3 @@ { | ||
}, | ||
"version": "0.1.1" | ||
"version": "0.1.2" | ||
} |
@@ -45,8 +45,9 @@ # karma-parallel | ||
config.set({ | ||
frameworks: ['mocha' /* or 'jasmine' */, 'parallel'], // this will load the framework and beforeMiddleware | ||
// NOTE: 'parallel' must be the first framework in the list | ||
frameworks: ['parallel', 'mocha' /* or 'jasmine' */, ], | ||
parallelOptions: { | ||
executors: 4, | ||
shardStrategy: 'round-robin' /* or 'description-length' */ | ||
executors: 4, // Defaults to cpu-count - 1 | ||
shardStrategy: 'round-robin' | ||
// shardStrategy: 'description-length' | ||
} | ||
@@ -74,3 +75,13 @@ }); | ||
## Important Notes | ||
**Why are there extra tests in my output?** | ||
If this plugin discovers that you have focused some tests (fit, it.only, etc...) in other browser instances, it will add an extra focused test in the current browser instance to limit the running of the tests in the given browser. Similarly, when dividing up the tests, if there are not enough tests for a given browser, it will add an extra test to prevent karma from failing due to no running tests. | ||
**What about code coverage?** | ||
Currently, code-coverage reports are not supported, although soon they will be. Stay tuned (or make a PR)! | ||
---- | ||
@@ -77,0 +88,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29086
467
108