/**
 * @head1 NAME
 *
 * BFTestRunner
 *
 * @head1 DESCRIPTION
 *
 * Instantiated globally as test_runner
 *
 * YUI custom assertions
 *
*/

var BFTestRunner = function() {
	this.__mock_objects = {};
	this.called = {};
	/**
	 * Initialize test runner by loading YUI files and test files.
	 * @constructor
	 */
	this.init = function() {
		test_runner.force_testmode_on_ajax();
		this.load_files([
			{type:'text/css',url:'http://yui.yahooapis.com/2.7.0/build/logger/assets/logger.css'},
			{type:'text/css',url:'http://yui.yahooapis.com/2.7.0/build/yuitest/assets/testlogger.css'},
			{type:'text/javascript',url:'http://yui.yahooapis.com/2.7.0/build/yahoo-dom-event/yahoo-dom-event.js'},
			{type:'text/javascript',url:'http://yui.yahooapis.com/2.7.0/build/logger/logger-min.js'},
			{type:'text/javascript',url:'http://yui.yahooapis.com/2.7.0/build/yuitest/yuitest-min.js'},
			{type:'text/javascript',url:'http://yui.yahooapis.com/2.7.0/build/yahoo/yahoo-min.js'},
			{type:'text/javascript',url:'http://yui.yahooapis.com/2.7.0/build/json/json-min.js'}
		]);
		this.observe('page_load:done', function() {
			test_runner.load_tests();
		});
		document.observe('dom:loaded', function(e){test_runner.fire_event('page_load',{})});
	}
	
	/**
	 * @head2 on_page
	 * 
	 * Return true if the specified page is loaded, otherwise load the specified page.
	 */
	this.on_page = function( page ) {
		if ( window.location.href.indexOf( page ) != -1 ) return true;
		else {
			var params = window.location.search;
			if ( page.indexOf('?') != -1 ) params = params.replace(/\?/,'&');
			window.location.href = BF_STATIC.web_root + page + params;
		}
		return false;
	}
	
	/* 
	 * @head2 append_to_function
	 *
	 * Execute additional code upon execution of specified function	
	 */
	this.append_to_function = function( old_fn, appendage ) {
		this.__mock_objects['special-' + old_fn] = eval( old_fn );
		this.mock( old_fn, function(){
			eval('var supr=' + test_runner.__mock_objects[old_fn]);
			supr(eval(old_fn+'.arguments'));
			appendage(eval(old_fn+'.arguments'));
		});
	}

	/*
	 * @head2 force_testmode_on_ajax
	 *
	 * Adds &testmode=on to url, so server knows to read from test database
	 */
	this.force_testmode_on_ajax = function( params ) {
		if ( typeof test_runner._request == 'undefined' ) {
			this._request = Ajax.Request;
			this._request_events = Ajax.Request.Events;
			this.mock('Ajax.Request', function(path, params){
				params.parameters.testmode='on';
				Ajax.Request.Events = test_runner._request_events;
				new test_runner._request(path, params);
			}, 'special');
		}
	}	

	/*
	 * @head2 remove_special
	 */
	this.remove_special = function( method ) {
		if ( typeof this.__mock_objects[method] != 'undefined' ) {
			eval(method + '=' + this.__mock_objects['special-' + method]);
		}
	}
	
	/*
	 * @head2 mock
	 *
	 * Create an object and assign the function. 
	 * Method should be passed in the form of a string and the function should be a function. 
	 * Eg: test_runner.mock('a.b.c.d', function(){});
	 */
	this.mock = function( method, fn, prefix) {
		if ( prefix ) prefix += '-' ;
		else prefix = '';
		this.__mock_objects[prefix + method] = eval( method );
		var object_path = method.split('.');
		var first_word = eval(object_path[0]);
		if ( typeof first_word == 'undefined' || typeof first_word == 'object' ) {
			this._mock_object( object_path, [] );
			object_path = method.split('.');
			var fn_name = object_path.pop() ;
			var obj = eval( object_path.join('.') );
			obj[fn_name] = fn;
		}
		else if ( typeof first_word == 'function' && object_path[1] ) {
			var obj = function(){};
			var old = first_word;
			for( var each in old.prototype ) {
				obj.prototype[each] = old.prototype[each];
			}
			obj.prototype[object_path[1]] = fn;
		}
		return obj;
	}
	
	/* Restore a mocked function to its previous definition */
	this.unmock = function( method ) {
		if ( typeof test_runner.__mock_objects[method] != 'undefined' ) {
			eval(method + '=' + test_runner.__mock_objects[method]);
		}
	}
	
	/* Restore all mocked functions to their previous definition */
	this.unmock_all = function(){
		for ( var method in test_runner.__mock_objects ) {
			if ( method.match(/^special-/) ) continue;
			test_runner.unmock( method );
		}
	}
	
	/*
	Recursive method to create the mock object.
	*/
	this._mock_object = function( object_to_build, object_built ) {
		object_built.push( object_to_build.shift() );
		var test;
		try {
			test = eval(object_built.join('.'));
		}
		catch ( e )
		{
			var str = object_built.join('.') + ' = new Object();';
			eval ( str );
		}
		if ( test == 'undefined' ) {
			var str = object_built.join('.') + ' = new Object();';
			eval ( str );
		}
		if ( object_to_build.length > 0 ) {
			this._mock_object( object_to_build, object_built );
		}
	}
	
	this._init_testmode = function(code) {
		// Wait till the test_runner object is loaded
		if ( typeof test_runner == 'undefined' ) {
			window.setTimeout(function(){test_runner._init_testmode(code)}, 200);
			return;
		}
		// hijack its run method to stash rather than run the suite
		test_runner.run = function(suite){code.suite=suite;};
		code.reload_count = 0;
		// hijack ajax requests to get responses from the suite rather than the server
		code.ajax = function(url,params) {
			try {
				var response = code.suite.get_response(url,params.parameters);
				// hide whatever would have been shown during ajax call
				if ( params.while_processing.show ) {
					$(params.while_processing.show).addClassName('hidden');
				}
				params.onSuccess({status:200,responseText:Object.toJSON(response)});
			} catch(e){
				if ( code.reload_count++ < 15 ) {
					window.setTimeout( function(){code.ajax(url,params)}, 200 );
				}
			}
		}
	}
	
	/*
	Auto generate simple tests for event handlers. 'tests' parameter should be a dictionary. Keys should be the
	name of the event handler (as a String), and values should be a list of dictionaries. Each of those dictionaries
	must have an id key, whose value is the ID of the element to click, and an optional args key, whose value is the
	arguments array that should be passed to the event handler.
	For example:
	test_runner.generate_event_handler_tests(suite,{
		'test_me.event_handler':[
			{id:'foo-btn',args:[1,2,3]},
			{id:'bar-btn',args:[4,5,6]}
		]
	})
	The above code would generate two tests, one to test that when $('foo-btn') is clicked, test_me.event_handler(1,2,3)
	is called. The second tests that when $('bar-btn') is clicked, test_me.event_handler(4,5,6) is called.
	The generated test is called "test_[btn ID]_is_hooked_up_to_[handler]_handler" and it tests to make sure the
	element to click is on the page, that when it's clicked the function is called, and that when the function is called
	the appropriate parameters are passed.
	*/
	this.generate_event_handler_tests = function(suite,tests) {
		var test_case = new YAHOO.tool.TestCase({name:'Auto-generated event handler tests'});
		for ( var handler in tests ) {
			tests[handler].each(function(btn){
				if ( typeof btn.el != 'undefined' ) {
					btn.id = btn.id || btn.el.className || btn.el.tagName;
				}
				else {
					btn.el = $(btn.id);
				}
				test_case['test_'+btn.id+'_is_hooked_up_to_' + handler + '_handler'] = function(handler,btn){
					if ( typeof btn.setup != 'undefined' ) btn.setup();
					var called = {};
					if ( typeof btn.preface != 'undefined' ) eval(btn.preface);
					test_runner.mock(handler, function(){
						called[handler] = true;
						called.arguments = arguments;
					});
					if ( btn.el == null ) btn.el = $(btn.id);
					test_runner.Assert.isObject(btn.el,'Found ' + btn.id + ' element on the page')
					test_runner.UserAction.click(btn.el);
					test_runner.Assert.areSame(true,called[handler],'Called ' + handler + ' from ' + btn.el.innerHTML + ' element ('+ btn.id +')')
					if ( btn.args ) {
						btn.args.each( function(expected,idx){
							test_runner.Assert.isDeeply(expected, called.arguments[idx],'Got expected argument in position ' + idx );
						});
					}
				}.curry(handler,btn);
			})
		}
		suite.add(test_case);
	}
	
	/* Auto-generate dependency tests. Pass dependencies as an array. If an array element is a string, the test
	  checks for the existance of an element with that ID; if it's an object, it creates a test based on the object.test
	  value. Currently, only 'is_defined' test type exists; this test makes sure the object specified in object.id
	  exists. (object.id should be a string that evals to the object to test for)
	*/
	this.generate_dependency_tests = function( suite, dependencies ) {
		var test_case = new YAHOO.tool.TestCase({name:'Auto-generated dependency tests'});
		dependencies.each( function(dependency){
			if ( typeof dependency == 'string' ) {
				test_case['test_'+dependency+'_element_id_exists'] = function(dependency){
					test_runner.Assert.elementIDExists(dependency);
				}.curry(dependency);
			}
			else if ( typeof dependency == 'object' ) {
				if ( dependency.test == 'is_defined' ) {
					test_case['test_'+dependency.id+'_is_defined'] = function(dependency){
						test_runner.Assert.isObject(eval(dependency.id),dependency.id + ' is loaded');
					}.curry(dependency);
				}
			}
		});
		suite.add(test_case);
	}
	
	/*
	Dump a test coverage profile to the console after tests have run.
	object_name ::= String (name of the instance of the object to profile)
	object ::= Object (instance of the object to profile)
	*/
	this.profile = function(object_name,object){		
		test_runner.call_profile = {};
		YAHOO.tool.TestRunner.subscribe(YAHOO.tool.TestRunner.COMPLETE_EVENT, function(){
			console.dir( { coverage : test_runner.call_profile } );
		});
		for ( var each in object ) {
			if ( typeof object[each] != 'function' ) continue;
			var rnd_name = each + '_' + (new Date()).getTime();
			object[rnd_name] = object[each];
			test_runner.call_profile[each] = 0;
			object[each] = Function(object[each].argumentNames(),'test_runner.call_profile["'+each+'"]+=1;'+object_name+'.'+rnd_name+'('+(object[each].argumentNames()).join(',')+')');
		}
	}
	
	var temp = BFW_Util.getCookie('bf_trace_path') 
	this.trace_path = (temp == null) ? new Array() : eval('('+unescape(temp)+')');
	this.trace = function(object_name) {
		var object = eval(object_name);
		var temp = {};
		for ( var key in object ) {
			temp[key] = object[key];
		}
		for ( var each in temp ) {
			if ( typeof object[each] != 'function' ) continue;
			if ( object[each].toString().match(/trace_wrapper/) ) continue;
			var rnd_name = each + '_' + (new Date()).getTime();
			object[rnd_name] = object[each];
			object[each] = Function(object[each].argumentNames(),'\
				var trace_wrapper = true; var __trace_context="unknown";\
				try{__trace_context = arguments.callee.caller.toString();}catch(e){} \
				var __trace_result;	\
				var __trace_e; \
				var __log = {args:arguments,calling:"'+object_name+'.'+each+'",context:__trace_context}; \
				test_runner.trace_path.push(__log); \
				console.dir({"calling_'+object_name+'.'+each+'":__log}); \
				try {\
					__trace_e="";\
					__trace_result='+object_name+'.'+rnd_name+'('+(object[each].argumentNames()).join(',')+');\
				} catch(e){__trace_e=e}; \
				__log = {error:__trace_e,_args:arguments,result:__trace_result,called:"'+object_name+'.'+each+'",context:__trace_context}; \
				test_runner.trace_path.push(__log); \
				console.dir({"called_'+object_name+'.'+each+'":__log}); \
				BFW_Util.setCookie({name:"bf_trace_path",value:escape(Object.toJSON(test_runner.trace_path))}); \
				if ( __trace_e != "" ) throw(__trace_e); \
				return __trace_result'
			);
		}
	}
	
	this.dump_trace = function() {
		var dump = window.open('','');
		test_runner.trace_path.each(function(entry){
			var str = '<li>';
			for( var each in entry ) {
				str += '<b>' + each + '</b>: ' + entry[each] + ' ('+typeof entry[each]+') <br />'
			}
			str += '</li>';
			dump.document.writeln(str);
		});
	}
	
	this.clear_trace = function() {
		BFW_Util.deleteCookie('bf_trace_path');
		test_runner.trace_path = new Array();
	}
	
	/*
	Execute second function once first returns true
	*/
	this.wait_until = function( fn_timer, fn_run, deadlock_suffix ) {
		if ( fn_timer() ) fn_run();
		else {
			if ( !deadlock_suffix ) {
				deadlock_suffix = (new Date()).getTime();
				Facebook['__deadlock_'+ deadlock_suffix] = 0;
			}
			else {
				if ( Facebook['__deadlock_' + deadlock_suffix]++ > 100 ) {
					//test_runner.Assert.areEqual(0,1,'Failed to evaluate wait_until() to true within a reasonable amount of time');
					fn_run();
				}
			}
			window.setTimeout(function(){test_runner.wait_until(fn_timer, fn_run, deadlock_suffix)}, 200);
		}
	}
	
	this.add = function( obj ) {
		var tr = YAHOO.tool.TestRunner;
		tr.add( obj );
	}	
	
	this.run = function(suite){
		this._run( true, suite );
	}
	
	this.local_run = function(suite){
		this._run( false, suite );
	}
	
	this._run = function(post_to_server, suite) {
		var tr = YAHOO.tool.TestRunner;
		tr.subscribe(tr.TEST_PASS_EVENT, test_runner.unmock_all);
		tr.subscribe(tr.TEST_FAIL_EVENT, test_runner.unmock_all);
		if ( post_to_server ) {
			tr.subscribe(tr.COMPLETE_EVENT, function(results){
				var oReporter = new YAHOO.tool.TestReporter(BF_STATIC.web_root + '/_fixtures', YAHOO.tool.TestFormat.JSON);
				oReporter.report(results.results);
			});
		}
		var logger = new YAHOO.tool.TestLogger();
		if ( window.location.href.match(/test_method=/i) ) {
			var params = window.location.href.split('&');
			params.each( function (args){
				if ( args.match(/test_method=/i) ) {
					var trash = args.split(/=/);
					var test_method = trash[1];
					suite.items.each( function(test){
						for ( var each in test ) {
							if ( each.match(/^test_/i) && each != test_method ) {
								delete test[each];
							}
						}
					})
				}
			});
		}
		if ( suite ) tr.add( suite );
		tr.run();
	}
	
	/*
	Set login cookie	
	*/
	this.log_in = function(session) {
		var default_session = {
			session_key : '1234567812345678123456781234567812345678123456781234567812345678marcos',
			display_name : 'Test%20User',
			image : '',
			username : 'marcos',
			facebook_uid : '',
			show_account_settings:'true',
			registration_path:'signin',
			is_facebook_user:'false',
			terminal_user:'false',
			last_active:123456789,
			isa:'false',
			user_id: 1234567
		};
		var obj = session ? session : {};
		for( var key in default_session ) {
			if ( typeof obj[key] == 'undefined' ) {
				obj[key] = default_session[key];
			}
		}
		BFW_Util.setCookie({name: BFW_COOKIE, value: obj.session_key });
		var info_cookie = 'display_name='+obj.display_name;
		info_cookie += '&image='+obj.image ;
		info_cookie += '&username='+obj.username;
		info_cookie += '&facebook_uid='+obj.facebook_uid ;
		info_cookie += '&show_account_settings=' + obj.show_account_settings;
		info_cookie += '&registration_path=' + obj.registration_path;
		info_cookie += '&is_facebook_user=' + obj.is_facebook_user;
		info_cookie += '&terminal_user=' + obj.terminal_user;
		info_cookie += '&isa=' + obj.isa;
		BFW_Util.setCookie({name: BFW_INFO_COOKIE, value: info_cookie });		
	}
	
	/*
	Remove login cookie
	*/
	this.log_out = function() {
		BFW_Util.deleteCookie(BFW_COOKIE);
		BFW_Util.deleteCookie(BFW_INFO_COOKIE);
		BFW_Util.deleteCookie(DEFAULT_USER);
	}
	
	/*
	Set up short cuts for common types of assertions, eg isVisible	
	*/
	this.extendAssertions = function() {
		test_runner.Assert = YAHOO.util.Assert;
		//test_runner.UserAction = YAHOO.util.UserAction;
		test_runner.UserAction = {};
		for( var each in YAHOO.util.UserAction ) {
			if ( each == 'click' ) {
				test_runner.UserAction[each] = function(obj) {
					if ( obj.click ) obj.click();
					else YAHOO.util.UserAction.click(obj);
				}
			}
			else {
				test_runner.UserAction[each] = YAHOO.util.UserAction[each];
			}
		}
		test_runner.Assert.isVisible = function( id, msg ) {
			var value = $(id).visible() && !$(id).hasClassName('hidden');
			test_runner.Assert.areEqual(true, value, msg ? msg : id + ' is visible');
		}
		test_runner.Assert.isHidden = function( id, msg ) {
			var value = $(id).visible() && !$(id).hasClassName('hidden');
			test_runner.Assert.areEqual(false, value, msg ? msg : id + ' is hidden');
		}
		test_runner.Assert.isLike = function( str, regex, msg ) {
			test_runner.Assert.isNotNull(str.match(regex), msg ? msg : str + ' matches regex');
		}
		test_runner.Assert.isNotLike = function( str, regex, msg ) {
			test_runner.Assert.isNull(str.match(regex), msg ? msg : str + ' matches regex');
		}
		test_runner.Assert.elementIDExists = function( id, msg ) {
			test_runner.Assert.isNotNull($(id), msg ? msg : id + ' is on the page');
		}
		test_runner.Assert.isDeeply = function( obj1, obj2, msg ) {
			if ( typeof obj1 == 'object' ) {
				for( var each in obj1 ) {
					test_runner.Assert.isDeeply( obj1[each], obj2[each], msg );
				}
			}
			else {
				test_runner.Assert.areSame( obj1, obj2, msg );
			}
		}
	}
	
	/*
	Load test files based on what's in the URL based on value[s] for test, tests, and test_suite.
	Value for test should match key in __tests, value for tests should match keys in __tests and
	be comma-delimited. Value for test_suite should match key in __test_suites.
	*/
	this.load_tests = function() {
		//YAHOO.tool.TestRunner.subscribe(YAHOO.tool.TestRunner.COMPLETE_EVENT,function(){alert('hi')});
		var params = test_runner.parse_params();
		var tests_to_run = params.test_suite ? test_runner.__test_suites[params.test_suite]() : [];
		if ( params.tests ) {
			var tests = params.tests.split(',');
			for( var i = 0; i < tests.length; i++ ) tests_to_run.push(test_runner.__tests[tests[i]]);
		}
		if ( params.test ) tests_to_run.push(test_runner.__tests[params.test]);
		test_runner._wait_for_yahoo(test_runner.extendAssertions);
		test_runner._wait_for_yahoo(function(){test_runner.load_files(tests_to_run)});
	}
	
	this._wait_for_yahoo = function(fn) {
		if ( typeof this.__wait_count == 'undefined' ) this.__wait_count = 0
		else if ( this.__wait_count++ > 1000 ) {
			alert('Took too long to load Yahoo files');
			return;
		}
		if ( typeof YAHOO == 'undefined'
			|| typeof YAHOO.util == 'undefined'
			|| typeof YAHOO.util.UserAction == 'undefined'
			|| typeof YAHOO.util.Assert == 'undefined' ) {
			window.setTimeout( function(e){test_runner._wait_for_yahoo(fn)}, 200 );
		}
		else { fn(); }
	}
	
	/*
	Get specified cookie value.
	*/
	this.cookie = function( key ) {
		var cookie_arr = document.cookie.split(';');
		var cookie = {};
		for( var i = 0; i < cookie_arr.length; i++ ) {
			var key_val = cookie_arr[i].split('=');
			key_val[0] = key_val[0].replace(/ /g,'');
			cookie[key_val[0]] = key_val[1];
		}
		return unescape(cookie[key]);
	}
	
	/*
	Take URL encoded string (query string or cookie) and break it into key/value pairs.
	*/
	this.parse_params = function(str) {
		var params = {};
		if ( !str )	str = window.location.search.substring(1);
		var key_values = str.split('&');
		for( var i = 0; i < key_values.length; i++ ) {
			var data = key_values[i].split('=');
			if ( data.length == 2) {
				params[data[0]] = data[1];
			}
			else {
				params[data[0]] = true;
			}
		}
		return params;
	}
	
	/*
	Return specified HTML element or, if it can't be found, look for it in DOM_SYNONYMS.
	This can be used to protect tests from element IDs changing. (In the test's setup,
	define DOM_SYNONYMS={old_id:new_id})
	*/
	this.$ = function(id) {
		if ( $(id) ) return $(id);
		return $(test_runner.DOM_SYNONYMS[id]);
	}
	
	/*
	Load specified file[s]. Unless otherwise specified, load as text/javascript.
	files can be a string representing a javascript file, a list of strings
	each of which represents a javascript file, or a list of objects with type
	specifying type (text/javascript or text/css), and url representing the file.
	*/
	this.load_files = function( files ) {
		if ( typeof files == 'string' ) files = [{type:'text/javascript',url:files}];
		for( var i = 0; i < files.length; i++ ) {
			var file_data = files[i];
			if ( typeof file_data == 'string') file_data = {type:'text/javascript',url:file_data};
			if ( file_data.type == 'text/javascript' ) this.load_javascript(file_data);
			else if ( file_data.type == 'text/css' ) this.load_css(file_data);
			else console.error( 'unrecognized file type to load: ' + file_data.type );
		}
	}
	
	/*
	Load javscript file
	*/
	this.load_javascript = function( data ) {
		var tag = document.createElement('script');
		tag.setAttribute('type', data.type);
		tag.setAttribute('src', data.url);
		tag.setAttribute('defer', 'false');
		if ( document.getElementsByTagName('HEAD') == 'undefined' ) {
			this.observe('page_load', function(){
				document.getElementsByTagName('HEAD')[0].appendChild(tag);
			});
		}
		else {
			document.getElementsByTagName('HEAD')[0].appendChild(tag);
		}
	}
	
	/*
	Load CSS file
	*/
	this.load_css = function( data ) {
		var tag = document.createElement('link');
		tag.setAttribute('rel', 'stylesheet');
		tag.setAttribute('type', data.type);
		tag.setAttribute('href', data.url);
		if ( document.getElementsByTagName('HEAD') == 'undefined') {
			this.observe('page_load', function(e){
				document.getElementsByTagName('HEAD')[0].appendChild(tag);
			});
		}
		else {
			document.getElementsByTagName('HEAD')[0].appendChild(tag);
		}
	}
	
	/*
	Set up event handler 
	*/
	this.observe = function(ev,fn) {
		if ( !this.__EVENTS[ev] ) this.__EVENTS[ev] = [];
		this.__EVENTS[ev].push(fn);
	}
	
	/*
	Fire event
	*/
	this.fire_event = function(ev, args) {
		var fns = test_runner.__EVENTS[ev] ? test_runner.__EVENTS[ev] : [];
		for( var i = 0; i < fns.length; i++ ) {
			fns[i](args);
		}
		if ( ev.indexOf( ':done' ) == -1 ) {
			test_runner.fire_event( ev+':done', args );
		}
	}
	
	this.__EVENTS = {};
	
	this.DOM_SYNONYMS = {};

	/* Test files */
	this.__tests = {
		'buzz_stats' : '/static/js/public/test/buzz_stats.js', 
		'demo' : '/static/js/public/test/demo.js',
		'login' : '/static/js/public/test/login.js'	,
		'register' : '/static/js/public/test/register.js',
		'profile' : '/static/js/public/test/profile.js',
		'prepopulated_quickpost' : '/static/js/public/test/prepopulated_quickpost_test.js',
		'save_buzz' : '/static/js/public/test/save_buzz_test.js',
		'facebook2' : '/static/js/public/test/facebook2_test.js',
		'badges' : '/static/js/public/test/badge_test.js',
		'session_tracking' : '/static/js/public/test/session_test.js',
		'micro_sites' : '/static/js/public/test/microsite_test.js',
		'handler_test' : '/static/js/public/test/generic_handler.js',
		'inline_media_social_actions' : '/static/js/public/test/inline-media-social-actions.js',
		'signup' : '/static/js/public/test/new_signup_page.js',
		'settings' : '/static/js/public/test/new_settings.js',
		'predictions' : '/static/js/public/test/predictions_test.js',
		'quickpost' : '/static/js/public/test/quickpost_test.js',
		'dashboard' : '/static/js/public/test/dashboard_test.js',
		'dashboard-stats' : '/static/js/public/test/dashboard-stats-test.js',
		'dashboard_ad' : '/static/js/public/test/dashboard_ad_test.js',
		'dashboard_widget' : '/static/js/public/test/dashboard_widget_test.js',
		'dashboard_user_stats' : '/static/js/public/test/dashboard_user_stats_test.js',
		'realtimeweb' : '/static/js/public/test/realtimeweb_test.js',
		'user_switching' : '/static/js/public/test/user_switching_test.js',
		'user_tracking' : '/static/js/public/test/user_tracking.js',
		'user_panel' : '/static/js/public/test/user_panel_test.js',
		'universal_dom_traversal' : '/static/js/public/test/universal_dom_traversal_test.js', 
		'social' : '/static/js/public/test/social_test.js', 
		'top_predictors' : '/static/js/public/test/top_predictors.js'
	};
	
	/*
	Test Suites return a list of files to load instead of a single file.
	*/
	this.__test_suites = {
		'all' : function(){
			var suite = [];
			for( var each in test_runner.__tests ) suite.push(test_runner.__tests[each]);
			return suite;
		}
	};
}

test_runner = new BFTestRunner();
if ( test_runner.parse_params()['testmode'] == 'on' ) {
	test_runner.init();
}


