Index: /CKTester/runners/yuitest/extension.js
===================================================================
--- /CKTester/runners/yuitest/extension.js	(revision 4145)
+++ /CKTester/runners/yuitest/extension.js	(revision 4146)
@@ -4,4 +4,212 @@
 */
 
+// Extending YUI TestCase functionality.
+( function()
+{
+	// Transform an array of values into object keys with the same value. 
+	YAHOO.lang.keysToObject = function( array , value )
+	{
+		var obj = {};
+		for( var i = 0 ; i < array.length ; i++ )
+		{
+			obj[ array[ i ] ] = value;
+		}
+		return i ? obj : null;
+	};
+
+	var native = YAHOO.tool.TestCase;
+
+	// Override the original constructor to wave in template processing logics. 
+	YAHOO.tool.TestCase = function( template )
+	{
+		// Construct the '_should' instructions object from any methods
+		// prefix by 'should'.
+		var nonInstructions = [ '_should', 'name', 'setUp', 'tearDown', /^test/ ],
+			should = template._should || ( template._should = {} ),
+			match,
+			instruction;
+		for ( var item in template )
+		{
+			if ( match = item.match( /^should(.*)/ ) )
+			{
+				instruction = match[ 1 ];
+				should[ instruction.charAt( 0 ).toLowerCase()
+					+ instruction.substr( 1 ) ] = YAHOO.lang.keysToObject( template[ item ], true );
+				delete template[ item ];
+			}
+		}
+
+		// Ignore all tests from the template other than the one specified by 'shouldIgnoreAllBut' (#4242).
+		var preserves;
+		if( ( preserves = template._should.ignoreAllBut ) )
+		{
+			delete template._should.ignoreAllBut;
+			var ignores = template._should.ignore = {}, match, test;
+			for ( test in template )
+				if ( ( match = test.match( /^test.*/ ) ) && !( match in preserves ) )
+					ignores[ match ] = true;
+		}
+
+		return native.call( this, template );
+	};
+	YAHOO.tool.TestCase.prototype = native.prototype;
+	YAHOO.tool.TestCase.Wait = native.Wait;
+
+	// Copy the whole YAHOO.tool.TestRuner._resumeTest codes here just to modify it for
+	// supporting optional error catching by instruction 'shouldThrows' (#4242).
+	YAHOO.tool.TestRunner._resumeTest = function (segment /*:Function*/) /*:Void*/
+	{
+
+            //get relevant information
+            var node /*:TestNode*/ = this._cur;
+            var testName /*:String*/ = node.testObject;
+            var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
+
+            //cancel other waits if available
+            if (testCase.__yui_wait){
+                clearTimeout(testCase.__yui_wait);
+                delete testCase.__yui_wait;
+            }
+
+            //get the "should" test cases
+            var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
+            var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
+			var shouldThrow /*:Object*/ = (testCase._should.throws || {})[testName];
+
+            //variable to hold whether or not the test failed
+            var failed /*:Boolean*/ = false;
+            var error /*:Error*/ = null;
+
+			var _resumeTestResult = CKEDITOR.tools.bind( function ( evt )
+			{
+				if( evt )
+					YAHOO.util.Event.removeListener( window, "error", arguments.callee );
+				//fireEvent appropriate event
+				if (failed) {
+					this.fireEvent(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
+				} else {
+					this.fireEvent(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
+				}
+
+				//run the tear down
+				testCase.tearDown();
+
+				//update results
+				node.parent.results[testName] = {
+					result: failed ? "fail" : "pass",
+					message: error ? error.getMessage() : "Test passed",
+					type: "test",
+					name: testName
+				};
+
+				if (failed){
+					node.parent.results.failed++;
+				} else {
+					node.parent.results.passed++;
+				}
+				node.parent.results.total++;
+
+				//set timeout not supported in all environments
+				if (typeof setTimeout != "undefined"){
+					setTimeout(function(){
+						YAHOO.tool.TestRunner._run();
+					}, 0);
+				} else {
+					this._run();
+				}
+
+			}, this );
+
+			//try the test
+            try {
+
+                //run the test
+                segment.apply(testCase);
+
+                //if it should fail, and it got here, then it's a fail because it didn't
+                if (shouldFail){
+                    error = new YAHOO.util.ShouldFail();
+                    failed = true;
+                } else if (shouldError){
+                    error = new YAHOO.util.ShouldError();
+                    failed = true;
+                }
+
+            } catch (thrown /*:Error*/){
+                if (thrown instanceof YAHOO.tool.TestCase.Wait){
+
+                    if (YAHOO.lang.isFunction(thrown.segment)){
+                        if (YAHOO.lang.isNumber(thrown.delay)){
+
+                            //some environments don't support setTimeout
+                            if (typeof setTimeout != "undefined"){
+                                testCase.__yui_wait = setTimeout(function(){
+                                    YAHOO.tool.TestRunner._resumeTest(thrown.segment);
+                                }, thrown.delay);
+                            } else {
+                                throw new Error("Asynchronous tests not supported in this environment.");
+                            }
+                        }
+                    }
+
+                    return;
+
+				}
+				else if (thrown instanceof YAHOO.util.AssertionError) {
+						if (!shouldFail){
+							error = thrown;
+							failed = true;
+						}
+					} else {
+						//first check to see if it should error
+						if (!shouldError) {
+							error = new YAHOO.util.UnexpectedError(thrown);
+							failed = true;
+						} else {
+							//check to see what type of data we have
+							if (YAHOO.lang.isString(shouldError)){
+
+								//if it's a string, check the error message
+								if (thrown.message != shouldError){
+									error = new YAHOO.util.UnexpectedError(thrown);
+									failed = true;
+								}
+							} else if (YAHOO.lang.isFunction(shouldError)){
+
+								//if it's a function, see if the error is an instance of it
+								if (!(thrown instanceof shouldError)){
+									error = new YAHOO.util.UnexpectedError(thrown);
+									failed = true;
+								}
+
+							} else if (YAHOO.lang.isObject(shouldError)){
+
+								//if it's an object, check the instance and message
+								if (!(thrown instanceof shouldError.constructor) ||
+										thrown.message != shouldError.message){
+									error = new YAHOO.util.UnexpectedError(thrown);
+									failed = true;
+								}
+
+							}
+
+						}
+					}
+
+				if( shouldThrow )
+				{
+					YAHOO.util.Event.addListener( window, 'error', _resumeTestResult );
+					throw thrown;
+				}
+				else
+					_resumeTestResult();
+
+            }
+
+			_resumeTestResult();
+
+        };
+} )();
+
 (function()
 {
@@ -17,4 +225,12 @@
 		div.innerHTML = text;
 	};
+
+	var runner = YAHOO.tool.TestRunner;
+	// Report runner status to cell.
+	if ( CKTESTER.cell )
+	{
+		runner.subscribe( runner.TEST_CASE_BEGIN_EVENT, CKTESTER.cell.start );
+		runner.subscribe( runner.TEST_CASE_COMPLETE_EVENT, CKTESTER.cell.complete );
+	}
 
 	var htmlEncode = function( data )
@@ -32,5 +248,4 @@
 	{
 		createLogger();
-		var runner = YAHOO.tool.TestRunner;
 
 		var handleTestResult = function( data )
@@ -66,11 +281,4 @@
 		runner.subscribe(runner.TEST_IGNORE_EVENT, handleTestResult);
 		runner.subscribe(runner.TEST_PASS_EVENT, handleTestResult);
-
-		if ( CKTESTER.cell )
-		{
-			runner.subscribe( runner.TEST_CASE_BEGIN_EVENT, CKTESTER.cell.start );
-			runner.subscribe( runner.TEST_CASE_COMPLETE_EVENT, CKTESTER.cell.complete );
-		}
-
 		// Whether control the runner manually instead of running on window onload.
 		!runner.defer && runner.run();
