Index: /CKTester/runners/yuitest/test.js
===================================================================
--- /CKTester/runners/yuitest/test.js	(revision 4132)
+++ /CKTester/runners/yuitest/test.js	(revision 4132)
@@ -0,0 +1,172 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://CKTESTER.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKTESTER.tools} object, which contains
+ * generic utility functions shared by all projects at this testing environment.
+ * Currently, our testing system is based on the <a href="http://developer.yahoo.com/yui/yuitest/">YUI Test</a>.
+ */
+
+
+CKTESTER.tools =
+{
+	/**
+	 * The assertion namespace, containing all assertion functions. Currently,
+	 * this is an alias for
+	 * <a href="http://developer.yahoo.com/yui/docs/YAHOO.util.Assert.html">YAHOO.util.Assert</a>.
+	 * @example
+	 * <b>CKTESTER.test.assert</b>.areEqual( '10', 10 );        // "true"
+	 * <b>CKTESTER.test.assert</b>.areSame( '10', 10 );         // "false"
+	 * <b>CKTESTER.test.assert</b>.isUndefined( window.test );  // "true"
+	 */
+	assert : YAHOO.util.Assert,
+
+	runner : YAHOO.tool.TestRunner,
+
+	/**
+	 * Adds a test case to the test runner.
+	 * @param {Object} testCase The test case object. See other tests for
+	 *		examples.
+	 * @example
+	 * <b>CKTESTER.test.addTestCase</b>((function()
+	 * {
+	 *     // Local reference to the "assert" object.
+	 *     var assert = CKTESTER.test.assert;
+	 *
+	 *     return {
+	 *         test_example : function()
+	 *         {
+	 *             assert.areSame( '10', 10 );  // FAIL
+	 *         }
+	 *      };
+	 * })());
+	 */
+	addTestCase : function( testCase )
+	{
+		YAHOO.tool.TestRunner.add( new YAHOO.tool.TestCase( testCase ) );
+	},
+
+	/**
+	 * Gets the inner HTML of an element, for testing purposes.
+	 * @param {Boolean} stripLineBreaks Assign 'false' to avoid trimming line-breaks.
+	 */
+	getInnerHtml : function( elementOrId , stripLineBreaks )
+	{
+		var html;
+
+		if ( typeof elementOrId == 'string' )
+			html = document.getElementById( elementOrId ).innerHTML;
+		else if ( elementOrId.getHtml )
+			html = elementOrId.getHtml();
+		else
+			html = elementOrId.innerHTML    // retrieve from innerHTML
+				   || elementOrId.value;    // retrieve from value
+
+		return CKTESTER.tools.fixHtml( html, stripLineBreaks );
+	},
+
+	fixHtml : function( html, stripLineBreaks )
+	{
+		html = html.toLowerCase();
+
+		if ( stripLineBreaks !== false )
+			html = html.replace( /[\n\r]/g, '' );
+		else
+			html = html.replace( /\r/g, '' );    // Normalize CRLF.
+
+		function sorter( a, b )
+		{
+			var nameA = a[ 0 ];
+			var nameB = b[ 0 ];
+			return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
+		}
+
+		html = html.replace( /<\w[^>]*/g, function( match )
+			{
+				var attribs = [];
+				var hasClass;
+
+				match = match.replace( /\s([^\s=]+)=((?:"[^"]*")|(?:'[^']*')|(?:[^\s]+))/g, function( match, attName, attValue )
+					{
+						if ( attName == 'style' )
+						{
+							// Reorganize the style rules so they are sorted by name.
+
+							var rules = [];
+
+							// Push all rules into an Array.
+							attValue.replace( /(?:"| |;|^ )\s*([^ :]+?)\s*:\s*([^;"]+?)\s*(?=;|"|$)/g, function( match, name, value )
+								{
+									rules.push( [ name, value ] );
+								});
+
+							// Sort the Array.
+							rules.sort( sorter );
+
+							// Transform each rule entry into a string name:value.
+							for ( var i = 0 ; i < rules.length ; i++ )
+								rules[ i ] = rules[ i ].join( ':' );
+
+							// Join all rules with commas, removing spaces and adding an extra comma to the end.
+							attValue = '"' + rules && ( rules.join( ';' ).replace( /\s+/g, '' ) + ';' );
+						}
+
+						// IE may have 'class' more than once.
+						if ( attName == 'class' )
+						{
+							if ( hasClass )
+								return '';
+
+							hasClass = true;
+						}
+
+						if ( attName != '_cke_expando' )
+							attribs.push( [ attName, attValue ] );
+
+						return '';
+					} );
+
+				attribs.sort( sorter );
+
+				var ret = match.replace( /\s{2,}/g, ' ' );
+
+				for ( var i = 0 ; i < attribs.length ; i++ )
+				{
+					ret += ' ' + attribs[i][0] + '=';
+					ret += (/^["']/).test( attribs[i][1] ) ? attribs[i][1] : '"' + attribs[i][1] + '"';
+				}
+
+				return ret;
+			} );
+
+		return html;
+	},
+
+	/**
+	 * Wrapper of CKTESTER.dom.element::getAttribute for style text normalization.
+	 * @param element
+	 * @param attrName
+	 */
+	getAttribute : function( element, attrName )
+	{
+		var retval = element.getAttribute( attrName );
+		if ( attrName == 'style' )
+		{
+			// 1. Lower case property name.
+			// 2. Add space after colon.
+			// 3. Strip whitepsaces around semicolon.
+			// 4. Always end with semicolon
+			return retval.replace( /(?:^|;)\s*([A-Z-_]+)(:\s*)/ig,
+				function( match, property, colon )
+				{
+					return property.toLowerCase() + ': ';
+				} )
+				.replace( /\s+(?:;\s*|$)/g, ';' )
+				.replace( /([^;])$/g, '$1;' );
+		}
+
+		return retval;
+	}
+};
