Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
162 lines (125 sloc) 3.57 KB
//
// Minimal test runner using observables that should work in any JavaScript
// environment. Works in NodeJs, PhantomJS and modern browsers.
//
//
// Observable
// ------------
var Observable = function(operation, arg) {
this.operation_ = operation;
if (operation === 'map') this.fn_ = arg;
if (operation === 'merge') this.mergeWith_ = arg;
this.observers_ = [];
};
Observable.prototype.register = function(observer) {
this.observers_.push(observer);
};
Observable.prototype.notify = function(evt) {
if (this.operation_ === 'map') {
var tmpEvt = this.fn_(evt);
evt = tmpEvt ? tmpEvt : evt;
}
this.observers_.forEach(function(o) {
o.notify(evt);
});
};
Observable.prototype.listen = function(element, event) {
element.addEventListener(event, this.notify.bind(this));
};
Observable.fromEvent = function(element, event) {
var observable = new Observable();
observable.listen(element, event);
return observable;
};
Observable.prototype.map = function(fn) {
var observable2 = new Observable('map', fn);
this.register(observable2);
return observable2;
};
Observable.prototype.merge = function(observable2) {
var observable3 = new Observable('merge', observable2);
this.register(observable3);
observable2.register(observable3);
return observable3;
};
//
// Test runner
// ------------
// use setImmediate if it exists, otherwise process.nextTick,
// otherwise setTimeout(fn,0) or just call the function
var immediate = function(fn) {
var run = (typeof setImmediate === 'function' && setImmediate) ||
(typeof process === 'object' && process.nextTick) ||  
(typeof setTimeout === 'function' && setTimeout) ||
null;
if (!run) return fn();
if (run === setTimeout) {
setTimeout(fn, 0);
return;
}
return run(fn);
};
// used by global exception handlers
initialized_ = false;
numInstances_ = 0;
TestObservable = function() {
Observable.call(this);
if (!initialized_) {
if (typeof process === 'object') {
process.on('uncaughtException',
handleEvent.bind(undefined, this));
process.on('exit',
handleEvent.bind(undefined, this));
} else if (typeof window === 'object') {
window.onerror = handleEvent.bind(undefined, this);
}
}
initialized_ = true;
numInstances_++;
};
// inherit Observable
TestObservable.prototype = Object.create(Observable.prototype);
TestObservable.basicFormat = function(e) {
if (e.type === 'error') {
return '\n-- Found error --\n' +
'Stack trace:\n' + e.stack + '\n' +
'Number of errors found: ' + e.numberOfErrors;
} else if (e.type === 'exit') {
return '\n-- Test summary --\n' +
'Number of tests: ' + e.numberOfTests + '\n' +
'Number of errors found: ' + e.numberOfErrors;
}
return e;
};
TestObservable.immediate = immediate;
var handleEvent = function(observable, e) {
var msg = {
numberOfTests: numInstances_
};
if (typeof e !== 'number') {
TestObservable.numErrors_++;
if (e.lineNumber) msg['lineNumber'] = e.lineNumber;
if (e.stack) msg['stack'] = e.stack;
msg['type'] = 'error';
msg['error'] = e;
} else {
msg['type'] = 'exit';
}
msg['numberOfErrors'] = TestObservable.numErrors_;
observable.notify(msg);
};
TestObservable.test = function(fn) {
observable = new TestObservable();
immediate(function() {
try {
fn();
} catch (e) {
handleEvent(observable, e);
}
});
return observable;
};
// static variable used accross instances
TestObservable.numErrors_ = 0;
if (typeof module === 'undefined') module = {};
module.exports = exports = TestObservable;