diff --git a/README.md b/README.md
index cfee3871ae..1127d0cec6 100755
--- a/README.md
+++ b/README.md
@@ -39,6 +39,7 @@ List of Definitions
* [bootstrap.datepicker](https://github.com/eternicode/bootstrap-datepicker) (by [Boris Yankov](https://github.com/borisyankov))
* [Box2DWeb](http://code.google.com/p/box2dweb/) (by [Josh Baldwin](https://github.com/jbaldwin/))
* [Breeze](http://www.breezejs.com/) (by [Boris Yankov](https://github.com/borisyankov))
+* [Browser Harness](https://github.com/scriby/browser-harness) (by [Chris Scribner](https://github.com/scriby))
* [CasperJS](http://casperjs.org) (by [Jed Hunsaker](https://github.com/jedhunsaker))
* [Cheerio](https://github.com/MatthewMueller/cheerio) (by [Bret Little](https://github.com/blittle))
* [Chosen](http://harvesthq.github.com/chosen/) (by [Boris Yankov](https://github.com/borisyankov))
diff --git a/browser-harness/browser-harness-tests.ts b/browser-harness/browser-harness-tests.ts
new file mode 100644
index 0000000000..c36a186cab
--- /dev/null
+++ b/browser-harness/browser-harness-tests.ts
@@ -0,0 +1,95 @@
+///
+
+import harness = module('browser-harness');
+
+harness.listen(4500);
+harness.listen(4500, function(){});
+harness.config.retryMS = 50;
+harness.config.timeoutMS = 1500;
+
+var browser = new harness.Browser({ type: 'chrome' });
+browser.open('http://localhost:8000/harness.html');
+browser.close();
+
+harness.events.on('ready', function(driver){
+ driver.events.on('console.log', function(text){
+ console.log(text);
+ });
+
+ driver.events.on('console.warn', function(text){
+ console.log(text);
+ });
+
+ driver.events.on('console.error', function(text){
+ console.log(text);
+ });
+
+ driver.events.on('window.onerror', function(text){
+ console.log(text);
+ });
+
+ driver.setUrl('http://localhost:8000');
+ driver.setUrl('http://localhost:8000', function(){});
+
+ var element = driver.findElement('body');
+ var html = element.html();
+ element.addClass('test').click();
+
+ driver.findElements('div').removeClass('test');
+
+ driver.findVisible('html').findVisible('body').toggleClass('test');
+ driver.findVisibles('div').hide().show();
+
+ driver.find('div').css('color', 'red', function(err, element){
+ element.hide().show();
+ });
+
+ driver.waitFor(function(){
+ return false;
+ });
+
+ driver.waitFor(function(){
+ return false;
+ }, function(){
+
+ });
+
+ driver.waitFor({
+ condition: function(){
+
+ },
+
+ exec: function(){
+
+ },
+
+ timeoutMS: 1000
+ });
+
+ driver.exec(function(){
+
+ });
+
+ driver.exec(function(){}, function(){});
+
+ driver.exec({ func: function(){}, args: [] });
+ driver.exec({ func: function(){}, args: [] }, function(){});
+});
+
+harness.events.once('ready', function(driver){
+ driver.events.once('console.log', function(text){
+ console.log(text);
+ });
+
+ driver.events.once('console.warn', function(text){
+ console.log(text);
+ });
+
+ driver.events.once('console.error', function(text){
+ console.log(text);
+ });
+
+ driver.events.once('window.onerror', function(text){
+ console.log(text);
+ });
+});
\ No newline at end of file
diff --git a/browser-harness/browser-harness.d.ts b/browser-harness/browser-harness.d.ts
new file mode 100644
index 0000000000..3a63899412
--- /dev/null
+++ b/browser-harness/browser-harness.d.ts
@@ -0,0 +1,133 @@
+// Type definitions for Browser Harness
+// Project: https://github.com/scriby/browser-harness
+// Definitions by: Chris Scribner
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+///
+
+declare module "browser-harness" {
+ import events = module("events");
+
+ interface HarnessEvents extends events.NodeEventEmitter {
+ once(event: string, listener: (driver: Driver) => void);
+ once(event: 'ready', listener: (driver: Driver) => void);
+
+ on(event: string, listener: (driver: Driver) => void);
+ on(event: 'ready', listener: (driver: Driver) => void);
+ }
+
+ interface DriverEvents extends events.NodeEventEmitter {
+ once(event: string, listener: (text: string) => void);
+ once(event: 'console.log', listener: (text: string) => void);
+ once(event: 'console.warn', listener: (text: string) => void);
+ once(event: 'console.error', listener: (text: string) => void);
+ once(event: 'window.onerror', listener: (text: string) => void);
+
+ on(event: string, listener: (text: string) => void);
+ on(event: 'console.log', listener: (text: string) => void);
+ on(event: 'console.warn', listener: (text: string) => void);
+ on(event: 'console.error', listener: (text: string) => void);
+ on(event: 'window.onerror', listener: (text: string) => void);
+ }
+
+ export interface Driver {
+ exec(args: { func: Function; args?: any[]}, callback?: Function) : any;
+ exec(func: Function, callback?: Function) : any;
+
+ setUrl(url: string, callback?: Function);
+
+ waitFor(args: { condition: Function; exec?: Function; timeoutMS?: number }, callback?: Function);
+ waitFor(condition: Function, callback?: Function);
+
+ findElement(selector: string, callback?: (err: Error, element: ElementProxy) => void): ElementProxy;
+ findElements(selector: string, callback?: (err: Error, elements: ElementProxy) => void): ElementProxy;
+
+ findVisible(selector: string, callback?: (err: Error, element: ElementProxy) => void): ElementProxy;
+ findVisibles(selector: string, callback?: (err: Error, elements: ElementProxy) => void): ElementProxy;
+ find(selector: string, callback?: (err: Error, elements: ElementProxy) => void): ElementProxy;
+
+ events: DriverEvents;
+ }
+
+ export interface ElementProxy {
+ click(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ focus(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ blur(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ val(value?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ attr(name: string, value?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ removeAttr(name: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ prop(name: string, value?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ removeProp(name: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ html(value?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ text(value?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ hasClass(className: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ addClass(className: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ removeClass(className: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ toggleClass(className: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+
+ trigger(event: string, extraParameters?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ triggerHandler(event: string, extraParameters?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+
+ css(name: string, value?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ height(value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ innerHeight(value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ outerHeight(value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ width(value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ innerWidth(value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ outerWidth(value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ offset(value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ position(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ scrollLeft(value?: number, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ scrollTop(value?: number, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+
+ hide(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ show(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ toggle(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+
+ children(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ closest(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ contents(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ find(selector: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ findElements(selector: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ findElement(selector: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ findVisible(selector: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ findVisibles(selector: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ isActionable(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ first(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ has(arg: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ is(arg: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ last(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ next(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ nextAll(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ nextUntil(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ offsetParent(callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ parent(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ parents(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ parentsUntil(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ prev(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ prevAll(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ prevUntil(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ siblings(selector?: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+
+
+ data(name: string, value?: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ removeData(name: string, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+
+ filter(selector: any, callback?: (err: Error, element: ElementProxy) => void) : ElementProxy
+ }
+
+ export class Browser {
+ //constructor(args: { type: string; location?: string; args?: string[] });
+ constructor(args: { type: string; location?: string; args?: any; });
+
+ open(harnessUrl: string, serverUrl?: string);
+ close();
+ }
+
+ export function listen(port: number, callback?: Function)
+ export var events: HarnessEvents;
+ export var config: {
+ timeoutMS: number;
+ retryMS: number;
+ };
+}
\ No newline at end of file