Skip to content

Instantly share code, notes, and snippets.

@mildmojo
Last active November 3, 2015 18:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mildmojo/c5753466efd74cb0622a to your computer and use it in GitHub Desktop.
Save mildmojo/c5753466efd74cb0622a to your computer and use it in GitHub Desktop.
Jasmine-node helpers: per-describe `setup` and `teardown` functions
/*
setup_teardown_helper.js
Setup and teardown helper functions for jasmine-node (jasmine 1.3).
IMNSHO, every damn test runner should provide the following hooks:
startup (global; runs once before any setups or specs begin)
setup (describe-local; runs once before any specs in a `describe`)
beforeEach (describe-local; runs once before each spec in a `describe`)
afterEach (describe-local; runs once after each spec in a `describe`)
teardown (describe-local; runs once after all specs in a `describe`)
shutdown (global; runs once after all specs and teardowns are finished)
This helper adds jasmine-node `setup` and `teardown` helpers to do work
before any specs and after all specs, respectively (ha!), inside a
`describe` block.
Jasmine-node looks for these helper scripts in a directory that ends in
'helper' or 'helpers' in the spec dir or spec parent dir (I think).
*/
(function(){
var specCounters = {};
// Monkeypatch `it` to keep track of how many specs are in the current `describe`.
var originalIt = jasmine.Env.prototype.it;
jasmine.Env.prototype.it = function() {
var suiteName = getSuiteName();
specCounters[suiteName] = specCounters[suiteName] || 0;
specCounters[suiteName]++;
originalIt.apply(this, arguments);
};
// Run the function argument once before any specs in this `describe` block.
// Timeout is an optional override.
global.setup = function(setupFunc, timeout) {
setupFunc._name = setupFunc.name || 'setup';
var wasCalled = false;
this.beforeEach(function() {
if (wasCalled) return;
asyncify(setupFunc, timeout).call(null, function setupDone() {
// Add code here if you need to do elapsed time or something.
});
wasCalled = true;
});
};
// Run the function argument once after all specs in this `describe` block.
// Timeout is an optional override.
global.teardown = function(teardownFunc, timeout) {
teardownFunc._name = teardownFunc.name || 'teardown';
this.afterEach(function() {
if (--specCounters[getSuiteName()] > 0) return;
asyncify(teardownFunc, timeout).call(null, function teardownDone() {
// Add code here if you need to do elapsed time or something.
});
});
};
// If `func` is sync, return it as an async function with one callback arg.
// If `func` is async, wrap it in jasmine runs/waitsFor and return it.
// Resulting function *can* be called synchronously.
function asyncify(func, timeout) {
var funcIsAsync = !!func.length;
function asyncWrapper(done) {
var isFinished = false;
var resultArgs;
runs(function runFunc() {
if (funcIsAsync) {
func.call(null, function() {
resultArgs = Array.prototype.slice.call(arguments);
isFinished = true;
});
} else {
resultArgs = func.call();
resultArgs = [].concat(resultArgs);
isFinished = true;
}
});
var waitingForMsg = 'function `' + (func.name || func._name) + '` to finish';
waitsFor(waitingForMsg, function() { return isFinished; }, timeout);
runs(function() {
if (typeof done === 'function') done.apply(null, resultArgs);
});
};
return asyncWrapper;
}
function getSuiteName() {
var env = jasmine.getEnv();
var currentSuiteDesc = env.currentSuite && env.currentSuite.description;
var currentSpecSuiteDesc = env.currentSpec && env.currentSpec.suite.description;
return currentSuiteDesc || currentSpecSuiteDesc;
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment