You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by br...@apache.org on 2009/09/26 15:22:32 UTC

svn commit: r819134 [2/2] - in /incubator/jspwiki/trunk/tests/javascript: ./ assets/ assets/images/

Added: incubator/jspwiki/trunk/tests/javascript/assets/json2.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/javascript/assets/json2.js?rev=819134&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/javascript/assets/json2.js (added)
+++ incubator/jspwiki/trunk/tests/javascript/assets/json2.js Sat Sep 26 13:22:31 2009
@@ -0,0 +1,478 @@
+/*
+    http://www.JSON.org/json2.js
+    2008-11-19
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or ' '),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+*/
+
+/*jslint evil: true */
+
+/*global JSON */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
+*/
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (!this.JSON) {
+    JSON = {};
+}
+(function () {
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z';
+        };
+
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('JSON.parse');
+        };
+    }
+})();

Propchange: incubator/jspwiki/trunk/tests/javascript/assets/json2.js
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/trunk/tests/javascript/jasmine-runner.html
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/javascript/jasmine-runner.html?rev=819134&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/javascript/jasmine-runner.html (added)
+++ incubator/jspwiki/trunk/tests/javascript/jasmine-runner.html Sat Sep 26 13:22:31 2009
@@ -0,0 +1,44 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <title>Jasmine Javascript Test Runner for JSPWiki</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+  <script type="text/javascript" src="./assets/jasmine-0.9.0.js"></script>
+  <script type="text/javascript" src="./assets/TrivialReporter.js"></script>
+
+  <!-- <script type="text/javascript" src="../../src/WebContent/scripts/mootools.js"></script> -->
+  <script src="../../src/WebContent/scripts/mootools-core.js"></script>
+  <script type="text/javascript" src="../../src/WebContent/scripts/mootools-more.js"></script>
+
+  <script type="text/javascript" src="../../src/WebContent/scripts/jspwiki-common.js"></script>
+  <script type="text/javascript" src="../../src/WebContent/scripts/jspwiki-edit.js"></script>
+  <script type="text/javascript" src="../../src/WebContent/scripts/dialog.js"></script>
+  <script type="text/javascript" src="../../src/WebContent/scripts/documoo/documoo.js"></script>
+
+  <script type="text/javascript">
+    jasmine.include('./spec-jspwiki-common.js', true);
+    jasmine.include('./spec-jspwiki-edit.js', true);
+    jasmine.include('./spec-dialog.js', true);
+    //jasmine.include('./spec-documoo.js', true);
+  </script>
+
+  <link href="./assets/jasmine-jspwiki.css" rel="stylesheet"/>
+</head>
+<body>
+<h1>Jasmine Javascript Test Runner <span>for JSPWiki</span></h1>
+<script type="text/javascript">
+
+  var jasmineEnv = jasmine.getEnv();
+  var trivialReporter = new jasmine.TrivialReporter();
+  jasmineEnv.addReporter(trivialReporter);
+  jasmineEnv.specFilter = function(spec) {
+    return trivialReporter.specFilter(spec);
+  };
+  window.onload = function() {
+    jasmineEnv.execute();
+  };
+
+</script>
+</body>
+</html>
\ No newline at end of file

Added: incubator/jspwiki/trunk/tests/javascript/spec-dialog.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/javascript/spec-dialog.js?rev=819134&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/javascript/spec-dialog.js (added)
+++ incubator/jspwiki/trunk/tests/javascript/spec-dialog.js Sat Sep 26 13:22:31 2009
@@ -0,0 +1,211 @@
+/*
+Script: spec-dialog.js
+	Test following classes: SnipEditor, Textarea, UndoRedo
+
+License:
+	xxx
+
+*/
+
+describe('Dialog & descendent Class specs', function(){
+
+	var tst, dialog;
+
+	beforeEach( function() {
+		$$('body')[0].adopt( tst = new Element('p') );
+		tst.set('html',"empty");
+		dialog = null;
+	});
+	afterEach( function(){
+
+		dialog = null; //fixme: remove Elements created by dialogs
+		tst.dispose();
+	});
+
+	//BASE DIALOG CLASS : test TODO
+
+
+	//SELECTION DIALOG
+	it('should turn a string into a selection dialog', function() {
+
+		var dialog = new SelectionDialog({
+			body:"left|center|right",
+			caption:"select",
+			relativeTo:tst,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		var els = dialog.body.getElements('li'), el = els[0];
+
+		expect(els.length).toEqual(3);
+		expect(el.get('text')).toEqual('left');
+		expect(el.get('title')).toEqual('left');
+
+		dialog.fireEvent('onSelect','newvalue');
+		expect(tst.get('text')).toEqual('newvalue');
+	});
+
+	it('should turn a array into a selection dialog', function() {
+		var dialog = new SelectionDialog({
+			body:['left','center'],
+			caption:"select",
+			relativeTo:tst,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		var els = dialog.body.getElements('li'), el = els[0];
+
+		expect(els.length).toEqual(2);
+		expect(el.get('text')).toEqual('left');
+		expect(el.get('title')).toEqual('left');
+	});
+
+	it('should turn an object into a selection dialog', function() {
+		var dialog= new SelectionDialog({
+			body:{Left:"left",Center:"center",Right:"right"},
+			caption:"select",
+			relativeTo:tst,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		var els = dialog.body.getElements('li'), el = els[2];
+
+		expect(els.length).toEqual(3);
+		expect(el.get('text')).toEqual('Right');
+		expect(el.get('title')).toEqual('right');
+	});
+
+	it('should hide the dialog when the autoClose flag is set', function(){
+
+		//the mockup Event object allows testing with keyboard and mouse events
+		Event.implement({
+			initialize: function(e){
+				//alert(JSON.encode(e));
+				for(var ee in e ){ this[ee] = e[ee]; }
+				//this.event=''; //used by snipeditor to check the event.which flag
+				return this;
+			},
+			stop: function(){ return this; }
+		});
+
+		var dialog= new SelectionDialog({
+			body:"left|center|right",
+			caption:"select",
+			relativeTo:tst,
+			showNow:false,
+			autoClose:true,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		expect(dialog.element.getStyle('display')).toEqual('none');
+
+		dialog.toggle();
+		expect(dialog.element.getStyle('display')).toNotEqual('none');
+
+		var el = dialog.body.getElements('li')[1];
+		dialog.onSelect({ 'type':'mousedown', target:el });
+		expect(tst.get('text')).toEqual('center');
+
+		expect(dialog.element.getStyle('display')).toEqual('none');
+	});
+
+	it('should make centered selection dialog', function(){
+		//fixme : getCoordinates seems not to run under JSSpec properly
+		var dialog= new SelectionDialog({
+			body:"left|center|right",
+			caption:"select",
+			relativeTo:tst, //should remove this line
+			autoClose:true,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		//todo validate position on the dialog
+	});
+
+
+	//FONT DIALOG
+	it('should generate a standard Font Dialog', function() {
+
+		var dialog= new FontDialog({
+			caption:"font",
+			relativeTo:tst,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		var els = dialog.body.getElements('li'), el = els[1];
+
+		expect(els.length).toEqual(9);
+		expect(el.get('text')).toEqual('Comic Sans');
+		expect(el.get('title')).toEqual('comic sans ms');
+
+		//grnch -- funny browser 'dependency'
+		if( Browser.Engine.webkit ){
+			expect(el.getStyle('font-family')).toEqual("'comic sans ms'");
+		} else {
+			expect(el.getStyle('font-family')).toEqual("comic sans ms");
+		}
+		dialog.fireEvent('onSelect','newvalue');
+		expect(tst.get('text')).toEqual('newvalue');
+	});
+
+	it('should generate a redefined Font Dialog', function() {
+
+		var dialog= new FontDialog({
+			fonts:{'Font name1':'font1', 'Font name2':'font2'},
+			caption:"font",
+			relativeTo:tst,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		var els = dialog.body.getElements('li'), el = els[1];
+
+		expect(els.length).toEqual(2);
+		expect(el.get('text')).toEqual('Font name2');
+		expect(el.get('title')).toEqual('font2');
+		expect(el.getStyle('font-family')).toEqual('font2');
+
+	});
+
+	//CHARS DIALOG
+	it('should generate a standard Chars Dialog', function() {
+
+		var dialog= new CharsDialog({
+			caption:"special chars",
+			relativeTo:tst,
+			onSelect:function(value){ tst.set('html', value ); }
+		});
+
+		var els = dialog.body.getElements("td"), el = els[11];
+
+		expect(els.length).toEqual(9*11);
+		//expect(el.get('text')).toEqual('&deg;');
+		expect(el.get('title')).toEqual('&deg;');
+
+		dialog.fireEvent('onSelect','newvalue');
+		expect(tst.get('text')).toEqual('newvalue');
+
+	});
+
+	//COLOR DIALOG
+
+	it('should generate a standard Color Dialog', function() {
+
+		var dialog= new ColorDialog({
+			relativeTo:tst,
+			onChange:function(value){ tst.set('html', value ); }
+		});
+
+		var img = dialog.body.getElements("img");
+
+		expect(img.length).toEqual(1);
+		expect(img[0].get('src')).toEqual('images/circle-256.png');
+		expect(dialog.color.get('text')).toEqual('#ffffff');
+		expect(dialog.cursor).toNotEqual( null );
+		expect(dialog.hsv).toEqual([0,0,100]);
+
+		dialog.fireEvent('onChange','newvalue');
+		expect(tst.get('text')).toEqual('newvalue');
+	});
+
+});
+

Added: incubator/jspwiki/trunk/tests/javascript/spec-documoo.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/javascript/spec-documoo.js?rev=819134&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/javascript/spec-documoo.js (added)
+++ incubator/jspwiki/trunk/tests/javascript/spec-documoo.js Sat Sep 26 13:22:31 2009
@@ -0,0 +1,201 @@
+/*
+Script: spec-documoo.js
+	Test following classes: Jsdocs
+
+License:
+	xxx
+
+*/
+
+describe('Documoo Class', function(){
+
+	var test, documoo;
+
+	beforeEach( function() {
+		$$('body')[0].adopt( test = new Element('div',{id:"TEST"}) );
+		test.set('html',"empty");
+
+		documoo = new Documoo();
+	});
+	afterEach( function(){
+		test.dispose();
+	});
+
+	it('should retain text without markup', function() {
+
+		var s = [
+			"/* Some text without markup. ",
+			"   End of text.*/"
+		].join('\n');
+
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( test.get('text') ).toEqual( " Some text without markup. \n   End of text." );
+
+	});
+
+	it('should format bold, italic and monospace text', function() {
+
+		var s = [
+			"/*	First a *bold* word or __two__ ",
+			"   More ''italic'' words are here",
+			"   And finally some {{monospaced}} text",
+			"	End of text.*/"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST b").length ).toEqual( 2 );
+		expect( $$("#TEST i").length ).toEqual(1);
+		expect( $$("#TEST tt").length ).toEqual(1);
+		expect( $$("#TEST b")[1].get('text') ).toMatch(/two/);
+		expect( $$("#TEST i")[0].get('text') ).toMatch(/italic/);
+		expect( $$("#TEST tt")[0].get('text') ).toMatch(/monospaced/);
+
+	});
+
+	it('should allow to escape markup with a tilde', function() {
+
+		var s = [
+			"/*	This is not a ~*bold~* word",
+			"	End of text.*/"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST b").length ).toEqual( 0 );
+		expect( $$("#TEST")[0].get('text') ).toMatch(/ \*bold\* /);
+
+	});
+
+	it('should format headings and sub-headings', function() {
+
+		var s = [
+			"/*	",
+			"Class: testClass",
+			"   Documentation text comes here",
+			"Arguments:",
+			"   This is a subheading",
+			"*/"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST h1").length ).toEqual( 1 );
+		expect( $$("#TEST h2").length ).toEqual( 1 );
+		expect( $$("#TEST h1")[0].get('text') ).toEqual("Class : testClass");
+		expect( $$("#TEST h1")[0].get('id') ).toMatch(/testClass/);
+		expect( $$("#TEST h2")[0].get('text') ).toEqual("Arguments:");
+		expect( $$("#TEST h2")[0].get('id') ).toEqual(null);
+
+	});
+
+	it('should format hyperlinks', function() {
+
+		var s = [
+			"/*	First line",
+			"   contains a http:www.google.com link",
+			"   and some [internal1] and <internal2> links",
+			"	End of text.*/"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST a").length ).toEqual( 3 );
+		expect( $$("#TEST a")[0].get('text') ).toMatch(/http:www.google.com/);
+		expect( $$("#TEST a")[0].get('href') ).toMatch(/http:www.google.com/);
+		expect( $$("#TEST a")[1].get('text') ).toMatch(/internal1/);
+		expect( $$("#TEST a")[1].get('href') ).toMatch(/#internal1/);
+		expect( $$("#TEST a")[2].get('text') ).toMatch(/internal2/);
+
+	});
+
+	it('should format unordered lists', function() {
+
+		var s = [
+			"/*   First line.",
+			"	* some list",
+			"	* some list",
+			"	  continues on next line",
+			"	* some list",
+			"	This text is not part of the list",
+			"	Second line",
+			"	- some other list",
+			"	- some other list",
+			"	End of text.*/"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST ul").length ).toEqual( 2 );
+		expect( $$("#TEST ul > li").length ).toEqual(5);
+		expect( $$("#TEST ul li").length ).toEqual(5);
+		expect( $$("#TEST ul li")[1].get('text') ).toMatch(/continues/);
+		expect( $$("#TEST ul li")[2].get('text') ).toNotMatch(/continues/);
+		expect( $$("#TEST ol").length ).toEqual(0);
+
+	});
+
+	it('should format ordered lists', function(){
+
+		var s = [
+			"/*   First line.",
+			"	# some list",
+			"	# some list",
+			"	  continues on next line",
+			"   End of Text",
+			"*/"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST ol").length ).toEqual( 1 );
+		expect( $$("#TEST ol li").length ).toEqual(2);
+		expect( $$("#TEST ol li")[1].get('text') ).toMatch(/continues/);
+		expect( $$("#TEST ul").length ).toEqual(0);
+
+	});
+
+	it('should format definition lists', function(){
+
+		var s = [
+			"/*	First line.",
+			"	fruit - Apples, oranges and lemons are fruits",
+			"	;vegetables: Tomatoes, carrots etc.",
+			"	   and indented text continues on next line",
+			"   this is not - a definition list",
+			"	End of text.*/"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST dl").length ).toEqual( 1 );
+		expect( $$("#TEST dl dd").length ).toEqual(2);
+		expect( $$("#TEST dl dt").length ).toEqual(2);
+		expect( $$("#TEST dl dt")[0].get('text') ).toMatch(/fruit/);
+		expect( $$("#TEST dl dd")[0].get('text') ).toNotMatch(/continues/);
+		expect( $$("#TEST dl dt")[1].get('text') ).toMatch(/vegetables/);
+		expect( $$("#TEST dl dd")[1].get('text') ).toMatch(/continues/);
+		expect( $$("#TEST dl dd")[1].get('text') ).toNotMatch(/End of/);
+
+	});
+
+	it('should preserve preformatted ordered lists', function(){
+		var pre = "This  is    *preformatted*      stuff";
+		var s = [
+			"/*	First line.",
+			"   > "+pre,
+			"   > "+pre,
+			"   > "+pre,
+			"",
+			"   {{{"+pre+"}}}",
+			"",
+			"(start code)",
+			pre,
+			"(end)",
+			"	End of text. */"
+		].join('\n');
+		test.set('html', documoo.formatDoc( 'test', s ) );
+
+		expect( $$("#TEST pre").length ).toEqual( 3 );
+		expect( $$("#TEST pre")[0].get('text') ).toEqual( pre+'\n'+pre+'\n'+pre );
+		expect( $$("#TEST pre")[1].get('text') ).toEqual( pre );
+		expect( $$("#TEST pre")[2].get('text') ).toEqual( pre );
+
+	});
+
+});
+

Added: incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-common.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-common.js?rev=819134&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-common.js (added)
+++ incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-common.js Sat Sep 26 13:22:31 2009
@@ -0,0 +1,863 @@
+/*
+Script: spec-jspwiki-common.js
+
+License:
+	http://www.apache.org/licenses/LICENSE-2.0
+*/
+
+describe('Basic Javascript specs', function(){
+
+	it('shoud convert to lower-case', function(){
+		expect('Hello'.toLowerCase()).toEqual('hello');
+	});
+    it('should concatenate two strings', function(){
+		expect("Hello " + "World").toEqual("Hello World");
+	});
+	it('should add two numbers', function(){
+		expect(1 + 2).toEqual(3);
+	});
+	it('should match array literals', function(){
+		expect([1,2,3]).toEqual([1,2,3]);
+	});
+	it('should difference of array literals', function(){
+		expect([1,2,3]).toNotEqual([4,5,6]);
+	});
+	it('should match date literals', function(){
+		expect(new Date(1979,03,27)).toEqual(new Date(1979,03,27));
+	});
+	it('should retrieve the DOM firstChild', function(){
+		expect(document.body.firstChild).toEqual(document.body.firstChild);
+	});
+});
+
+describe('Common mootools extensions', function(){
+
+	it('should convert camel-case into space separated sentences', function(){
+
+		expect('thisIsCamelCase'.deCamelize()).toEqual('this Is Camel Case');
+		expect('thisIsACamelCase'.deCamelize()).toEqual('this Is ACamel Case');
+
+	});
+
+	it('should truncate a string to a maximum size', function(){
+
+		expect('test'.trunc(6)).toEqual('test');
+		expect('test a longer string'.trunc(6)).toEqual('test a...');
+		expect('test a longer string'.trunc(6,"---")).toEqual('test a---');
+
+	});
+
+	it('should return the text value of a DOM element', function(){
+
+		var a = new Element('div');
+		a.innerHTML= "test string";
+
+		expect( $getText(a) ).toEqual('test string');
+
+	});
+
+	it('should return the last item in the array', function(){
+
+    	expect([].getLast()).toEqual(null);
+    	expect([3].getLast()).toEqual(3);
+	    expect([1,2,3,0,0,6].getLast()).toEqual(6);
+
+	});
+ 
+	it('should put an wrapper around a set of DOM elements', function(){
+		var html = "<div>some text</div>Some plain text<span>more text</span><p>And more text</p>";
+		var div = new Element('div').set('html',html);
+		var wrap = new Element('p').wrapContent(div);
+
+		expect(wrap.innerHTML).toEqual(html);
+		expect(div.innerHTML).toEqual("<p>"+html+"</p>");
+
+	});
+
+	it('should return the visibility of an DOM element, depending on the visibility of one of its parents', function(){
+
+		var span = new Element('span').set('html','Inside text');
+		var div = new Element('div',{'styles':{'display':'none'}}).adopt(span);
+		expect(span.visible()).toEqual(false);
+
+		div.setStyle('display','block');
+		expect(span.visible()).toEqual(true);
+	});
+
+	it('should return the default value of any form element', function(){
+
+		var form = new Element('form').set('html',[
+			"<input name='input' type='checkbox' checked='checked'></input>",
+			"<select name='select'>",
+			"<option value='volvo'>Volvo</option>",
+			"<option value='saab' selected='true'>Saab</option>",
+			"</select>",
+			"<textarea name='textarea'>textarea-value</textarea>"
+		].join(''));
+		expect( form.input.getDefaultValue() ).toBeFalsy();
+		expect( form.select.getDefaultValue() ).toEqual('saab');
+		expect( form.textarea.getDefaultValue() ).toEqual('textarea-value');
+
+		//now change the value of the form elements
+		form.input.checked = true;
+		form.select.selectedIndex = 0;
+		form.textarea.value = "new textarea-value";
+
+		expect( form.input.get('value') ).toEqual('on'); //strange return value FIXME
+		expect( form.select.get('value') ).toEqual('volvo');
+		expect( form.textarea.get('value') ).toEqual('new textarea-value');
+
+		//the default values remain unchanged
+		expect( form.input.getDefaultValue() ).toBeFalsy();
+		expect( form.select.getDefaultValue() ).toEqual('saab');
+		expect( form.textarea.getDefaultValue() ).toEqual('textarea-value');
+	});
+
+	it('should return a localized string', function(){
+
+		LocalizedStrings = {
+			'javascript.moreInfo' : 'More',
+			'javascript.imageInfo' : 'Image {0} of {1}'
+		};
+
+		expect( "moreInfo".localize() ).toEqual('More');
+		expect( "imageInfo".localize(2) ).toEqual('Image 2 of ???1???');
+		expect( "imageInfo".localize(2,4) ).toEqual('Image 2 of 4');
+		expect( "imageInfo".localize(2,4,6) ).toEqual('Image 2 of 4');
+		expect( "funny string".localize() ).toEqual('???funny string???');
+	});
+
+});
+
+
+
+
+describe('Main WIKI class',function(){
+
+	it('should convert a pagename into a wiki url', function(){
+
+		Wiki.PageUrl = '/JSPWiki-pipo/Wiki.jsp?page=%23%24%25';
+
+		expect(Wiki.toUrl('test')).toEqual('/JSPWiki-pipo/Wiki.jsp?page=test');
+	});
+
+	it('should convert a wiki url into a pagename', function(){
+
+		Wiki.PageUrl = '/JSPWiki-pipo/Wiki.jsp?page=%23%24%25';
+
+		expect(Wiki.toPageName('/JSPWiki-pipo/Wiki.jsp?page=test')).toEqual('test');
+		expect(Wiki.toPageName('test')).toBeFalsy();
+	});
+
+	it('should remove funny chars to make a valid wiki pagename', function(){
+
+		expect(Wiki.cleanPageName('  ab    cd  ')).toEqual('ab cd');
+		expect(Wiki.cleanPageName('a1b2c3()&+,-=._$d4e5f6')).toEqual('a1b2c3()&+,-=._$d4e5f6');
+		expect(Wiki.cleanPageName('ab%@!\\?cd')).toEqual('abcd');
+	});
+
+});
+
+
+describe('WikiSlimbox class',function(){
+});
+
+describe('TabbedSection class',function(){
+});
+
+describe('Searchbox class',function(){
+});
+
+describe('Color class',function(){
+
+	it('hex constructor - should have the correct rgb', function(){
+		var myColor = new Color('#a2c240');
+		var myColor2 = new Color([162, 194, 64]);
+
+		//expect(myColor).toEqual([162, 194, 64]);
+		expect(myColor).toEqual(myColor2);
+	});
+
+	it('hex constructor - should have the correct hex', function(){
+		var myColor = new Color('#a2c240');
+		expect(myColor.hex).toEqual('#a2c240');
+
+		myColor = new Color('#666');  //short notation
+		expect(myColor.hex).toEqual('#666666');
+	});
+
+	it('rgb constructor - should have the correct rgb', function(){
+		var myColor = new Color([162, 194, 64],'rgb');
+		var myColor2 = new Color([162, 194, 64]);
+
+		expect(myColor).toEqual(myColor2);
+	});
+
+	it('rgb constructor - should have the correct hex', function(){
+		myColor = new Color([162, 194, 64],'rgb');
+		expect(myColor.hex).toEqual('#a2c240');
+
+		myColor = new Color([162, 194, 64]);
+		expect(myColor.hex).toEqual('#a2c240');
+	});
+
+	it('html-name constructor - should have the correct hex', function(){
+		var myColor = new Color('lime');
+
+		expect(myColor.hex).toEqual('#00ff00');
+		expect(myColor).toEqual(new Color([0, 255, 0]));
+	});
+
+	it('should mix two colors', function(){
+		var myColor = new Color('#000').mix('#fff', [255, 0, 255], 10);
+		expect(myColor.hex).toEqual('#311731');
+	});
+
+	it('should invert a color', function(){
+		var white = new Color('white');
+		var black = new Color('black');
+
+		expect(white.invert().hex).toEqual( black.hex );
+	});
+});
+
+
+describe('GraphBar class',function(){
+
+	var graph;
+
+	//helperfunction to build graphbars.
+	var buildTable = function(clazzname, markup){
+
+		var html = markup.map(function(row){
+			row = row.replace( /\|.[^|]*/g, function(match){
+				return (match.charAt(1)=='|') ? '<th>'+match.slice(2)+'</th>' : '<td>'+match.slice(1)+'</td>';
+			});
+			return '<tr>'+row+'</tr>';
+		});
+
+		graph.empty().removeClass('graphBars').addClass(clazzname)
+			.adopt( new Element('table',{'html':html}) );
+
+		new GraphBar(graph);
+	};
+
+	beforeEach( function(){
+
+		document.body.adopt( graph = new Element('div',{'class':'graphBars'}) );
+
+	});
+
+	afterEach( function(){
+
+		graph.dispose();
+
+	});
+
+	it('should generate inline horizontal bars',function(){
+//		%%graphBars
+//		* This is the 1st bar: %%gBar 100 /%
+//		* This is the 2nd bar: %%gBar 120 /%
+//		* This is the 3rd bar: %%gBar 140 /%
+//		/%
+		graph.empty().adopt(
+			new Element('ul').adopt(
+				new Element('li',{html:"This is the 1st bar: <span class='gBar'>100</span>"}),
+				new Element('li',{html:"This is the 2nd bar: <span class='gBar'>120</span>"}),
+				new Element('li',{html:"This is the 3rd bar: <span class='gBar'>140</span>"})
+			)
+		);
+		//run render graphbar :
+		new GraphBar(graph);
+
+		expect( $$('span.graphBar').length).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+
+	});
+
+	it('should parse data in tables, with %%gBar',function(){
+//		%%graphBars
+//		| apples  | 20 kg
+//		| pears   | 40 kg
+//		| bananas | 60 kg
+//		/%
+		buildTable("graphBars", [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>40 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+
+	});
+
+	it('should parse column data in tables, with names',function(){
+//		%%graphBarsWeight
+//		|| Name   ||Weight
+//		| apples  | 20 kg
+//		| pears   | 40 kg
+//		| bananas | 60 kg
+//		/%
+		buildTable('graphBarsWeight', [
+			"|| Name   || Weight ",
+			"| apples  | 20 kg ",
+			"| pears   | 40 kg ",
+			"| bananas | 60 kg "]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+
+	});
+
+	it('should parse row data in tables, with names',function(){
+//		%%graphBarsWeight
+//		||Name    | apples | pears | bananas
+//		||Weight  | 20 kg  | 40 kg | 60
+//		/%
+		buildTable('graphBarsWeight', [
+			"||Name    | apples | pears | bananas",
+			"||Weight  | 20 kg  | 40 kg | 60 "
+		]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+
+
+	});
+
+	it('should do vertical bars',function(){
+//		%%graphBars-vertical
+//		| apples  | %%gBar 20 kg /%
+//		| pears   | %%gBar 40 kg /%
+//		| bananas | %%gBar 60 kg /%
+//		/%
+
+		buildTable('graphBars-vertical', [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>40 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> " ]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-bottom-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[0].getStyle("position") ).toEqual("absolute");
+		expect( $$('span.graphBar')[0].getStyle("width") ).toEqual("20px");
+		expect( $$('span.graphBar')[0].getStyle("bottom") ).toEqual("0px");
+		expect( $$('span.graphBar')[0].getParent().getStyle("position") ).toEqual("relative");
+		expect( $$('span.graphBar')[0].getNext().getStyle("position") ).toEqual("relative");
+		expect( $$('span.graphBar')[0].getNext().getStyle("top") ).toEqual("300px");
+
+
+		expect( $$('span.graphBar')[1].getStyle("border-bottom-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[1].getStyle("position") ).toEqual("absolute");
+		expect( $$('span.graphBar')[1].getStyle("width") ).toEqual("20px");
+		expect( $$('span.graphBar')[1].getStyle("bottom") ).toEqual("0px");
+		expect( $$('span.graphBar')[1].getParent().getStyle("position") ).toEqual("relative");
+		expect( $$('span.graphBar')[1].getNext().getStyle("position") ).toEqual("relative");
+		expect( $$('span.graphBar')[1].getNext().getStyle("top") ).toEqual("150px");
+
+		expect( $$('span.graphBar')[2].getStyle("border-bottom-width") ).toEqual("320px");
+		expect( $$('span.graphBar')[2].getStyle("position") ).toEqual("absolute");
+		expect( $$('span.graphBar')[2].getStyle("width") ).toEqual("20px");
+		expect( $$('span.graphBar')[2].getStyle("bottom") ).toEqual("0px");
+		expect( $$('span.graphBar')[2].getParent().getStyle("position") ).toEqual("relative");
+		expect( $$('span.graphBar')[2].getNext().getStyle("position") ).toEqual("relative");
+		expect( $$('span.graphBar')[2].getNext().getStyle("top") ).toEqual("0px");
+
+	});
+
+	it('should generate date&time graph-bars',function(){
+//		%%graphBars
+//		|| Name   ||Weight
+//		| apples  | %%gBar 1 Jan 2006 /%
+//		| pears   | %%gBar 15 Feb 2006 /%
+//		| bananas | %%gBar 1 Apr 2006 /%
+//		/%
+		buildTable("graphBars", [
+			"| apples  | <span class='gBar'>1 Jan 2006 </span> ",
+			"| pears   | <span class='gBar'>20 Feb 2006</span> ",
+			"| bananas | <span class='gBar'>1 Apr 2006 </span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("186px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+
+	});
+
+	it('should accept single color input',function(){
+//		%%graphBars-fuchsia
+//		| apples  | %%gBar 20 kg /%
+//		| pears   | %%gBar 40 kg /%
+//		| bananas | %%gBar 60 kg /%
+//		/%
+		buildTable("graphBars-fuchsia", [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>40 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[0].getStyle("border-left-color") ).toEqual("#ff00ff");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-color") ).toEqual("#ff00ff");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-color") ).toEqual("#ff00ff");
+
+	});
+
+	it('should accept double color input to generate gradient colors for all bars',function(){
+//		%%graphBars-ffff00-669900
+//		| apples  | %%gBar 20 kg /%
+//		| pears   | %%gBar 40 kg /%
+//		| bananas | %%gBar 60 kg /%
+//		/%
+		buildTable("graphBars-ffff00-669900", [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>40 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[0].getStyle("border-left-color") ).toEqual("#ffff00");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-color") ).toEqual("#b3cc00");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-color") ).toEqual("#669900");
+
+	});
+
+	it('should invert color if progress bars specify only one color',function(){
+//		%%graphBars-00ffff-progress
+//		| apples  | %%gBar 20 kg /%
+//		| pears   | %%gBar 40 kg /%
+//		| bananas | %%gBar 60 kg /%
+//		/%
+		buildTable("graphBars-red-progress", [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>40 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(6);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[0].getStyle("border-left-color") ).toEqual("#00ffff");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("300px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-color") ).toEqual("#ff0000");
+		expect( $$('span.graphBar')[1].getStyle("margin-left") ).toEqual("-1ex");
+
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-color") ).toEqual("#00ffff");
+		expect( $$('span.graphBar')[3].getStyle("border-left-width") ).toEqual("150px");
+		expect( $$('span.graphBar')[3].getStyle("border-left-color") ).toEqual("#ff0000");
+		expect( $$('span.graphBar')[3].getStyle("margin-left") ).toEqual("-1ex");
+
+		expect( $$('span.graphBar')[4].getStyle("border-left-width") ).toEqual("320px");
+		expect( $$('span.graphBar')[4].getStyle("border-left-color") ).toEqual("#00ffff");
+		expect( $$('span.graphBar')[5].getStyle("border-left-width") ).toEqual("0px");
+		expect( $$('span.graphBar')[5].getStyle("border-left-color") ).toEqual("#ff0000");
+		expect( $$('span.graphBar')[5].getStyle("margin-left") ).toEqual("-1ex");
+
+	});
+
+	it('should accept min and max bar size',function(){
+//		%%graphBars-min48-max256
+//		| apples  | %%gBar 20 kg /%
+//		| pears   | %%gBar 40 kg /%
+//		| bananas | %%gBar 60 kg /%
+//		/%
+		buildTable("graphBars-min48-max256", [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>40 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("48px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("152px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("256px");
+
+	});
+
+	it('should do progress bars, with 2 complementary bars reaching 100%',function(){
+//		%%graphBars-fff00-669900-progress
+//		|| Name   ||Weight
+//		| apples  | %%gBar 20 kg /%
+//		| pears   | %%gBar 40 kg /%
+//		| bananas | %%gBar 60 kg /%
+//		/%
+		buildTable("graphBars-ffff00-669900-progress", [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>40 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(6);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[0].getStyle("border-left-color") ).toEqual("#669900");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("300px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-color") ).toEqual("#ffff00");
+		expect( $$('span.graphBar')[1].getStyle("margin-left") ).toEqual("-1ex");
+
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("170px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-color") ).toEqual("#669900");
+		expect( $$('span.graphBar')[3].getStyle("border-left-width") ).toEqual("150px");
+		expect( $$('span.graphBar')[3].getStyle("border-left-color") ).toEqual("#ffff00");
+		expect( $$('span.graphBar')[3].getStyle("margin-left") ).toEqual("-1ex");
+
+		expect( $$('span.graphBar')[4].getStyle("border-left-width") ).toEqual("320px");
+		expect( $$('span.graphBar')[4].getStyle("border-left-color") ).toEqual("#669900");
+		expect( $$('span.graphBar')[5].getStyle("border-left-width") ).toEqual("0px");
+		expect( $$('span.graphBar')[5].getStyle("border-left-color") ).toEqual("#ffff00");
+		expect( $$('span.graphBar')[5].getStyle("margin-left") ).toEqual("-1ex");
+
+	});
+
+	it('should do gauge bars, with colors relative to the values',function(){
+//		%%graphBars-fff00-669900-gauge
+//		|| Name   ||Weight
+//		| apples  | %%gBar 20 kg /%
+//		| pears   | %%gBar 50 kg /%
+//		| bananas | %%gBar 60 kg /%
+//		/%
+		buildTable("graphBars-ffff00-669900-gauge", [
+			"| apples  | <span class='gBar'>20 kg</span> ",
+			"| pears   | <span class='gBar'>50 kg</span> ",
+			"| bananas | <span class='gBar'>60 kg</span> "]);
+
+		expect( $$('span.graphBar').length ).toEqual(3);
+		expect( $$('span.graphBar')[0].getStyle("border-left-width") ).toEqual("20px");
+		expect( $$('span.graphBar')[0].getStyle("border-left-color") ).toEqual("#ffff00");
+		expect( $$('span.graphBar')[1].getStyle("border-left-width") ).toEqual("245px");
+		expect( $$('span.graphBar')[1].getStyle("border-left-color") ).toEqual("#8cb300");
+		expect( $$('span.graphBar')[2].getStyle("border-left-width") ).toEqual("320px");
+		expect( $$('span.graphBar')[2].getStyle("border-left-color") ).toEqual("#669900");
+
+	});
+
+});
+
+describe('Collapsible class',function(){
+});
+
+describe('TableAdds class',function(){
+
+	var myTable,row,getcol,s;
+
+	beforeEach( function(){
+		document.body.adopt( myTable = new Element('table') );
+
+		//rowbuilder help function
+		row = function(r){
+			return ("<tr><td>"+r.split('|').join('</td><td>')+"</td></tr>");
+		};
+		//return column as js-array of values
+		getcol = function(column){
+			var result = [];
+			$A( myTable.rows ).each( function(r,i){
+				if( (i>0) && (r.style.display != 'none')) result.push( $(r.cells[column]).get('text').trim() );
+			});
+			return result;
+		};
+
+		var s = '';
+		s+= row('Title  | Author  | Published    | Edition  | Some IP@         | Expenses  | Disk Memory|Float').replace(/td>/g,"th>");
+		s+= row('book1  |  zappy  |  25 Feb 2005 |  5       |  100.100.100.100 |  €500     |  2Gb       |0.01');
+		s+= row('book2  |  happy  |  25 Jan 2005 |  19      |  256.100.100.100 |  €1500    |  4Kb       |10e5');
+		s+= row('book3  |  pappy  |  23 Mar 2005 |  06      |  10.100.100.100  |  €50      |  1.23TB    |-1e3');
+		s+= row('book4  |  dappy  |  21 Apr 2005 |  199     |  1.100.100.100   |  €0.500   |  2.73kb    |0.443e-12');
+		s+= row('book5  |  pappy  |  25 Jul 2005 |  017     |  1.100.25.100    |  €5500    |  0.4Tb     |0.1e-99');
+
+		myTable.set('html',s);
+	});
+
+	afterEach( function(){
+		myTable.dispose();
+	});
+
+	it('should add default color zebra stripes to a table', function(){
+		new TableAdds(myTable, {zebra:['table']});
+
+		var rows = myTable.rows;
+		expect( rows[1].hasClass('odd') ).toBeFalsy();
+		expect( rows[2].hasClass('odd') ).toBeTruthy();
+		expect( rows[3].hasClass('odd') ).toBeFalsy();
+
+	});
+	it('should add one color zebra stripes to a table', function(){
+		new TableAdds(myTable, {zebra:['#fffff']});
+
+		var rows = myTable.rows;
+		expect( rows[1].getStyle('background-color') ).toEqual('transparent');
+		expect( rows[2].getStyle('background-color') ).toEqual('#ffffff');
+		expect( rows[3].getStyle('background-color') ).toEqual('transparent');
+
+	});
+
+	it('should add 2 color zebra stripes to a table', function(){
+		new TableAdds(myTable, {zebra:['#fffff','lime']});
+
+		var rows = myTable.rows;
+		expect( rows[1].getStyle('background-color') ).toEqual('#00ff00');
+		expect( rows[2].getStyle('background-color') ).toEqual('#ffffff');
+		expect( rows[3].getStyle('background-color') ).toEqual('#00ff00');
+
+	});
+
+	it('should sort integer numbers', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 3, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['5', '19', '06', '199', '017'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toEqual( ['5', '06', '017', '19', '199'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toEqual( ['199', '19', '017', '06', '5' ] );
+
+	});
+
+	it('should sort dates', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 2, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['25 Feb 2005', '25 Jan 2005', '23 Mar 2005', '21 Apr 2005', '25 Jul 2005'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toEqual( ['25 Jan 2005',  '25 Feb 2005', '23 Mar 2005', '21 Apr 2005', '25 Jul 2005'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toEqual( ['25 Jul 2005', '21 Apr 2005', '23 Mar 2005', '25 Feb 2005', '25 Jan 2005'] );
+
+	});
+	it('should sort currency', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 5, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['€500', '€1500', '€50', '€0.500', '€5500'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toEqual( ['€0.500', '€50', '€500', '€1500', '€5500'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toEqual( ['€5500', '€1500', '€500', '€50', '€0.500'] );
+
+	});
+	it('should sort ip@', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 4, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['100.100.100.100', '256.100.100.100', '10.100.100.100', '1.100.100.100', '1.100.25.100'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toEqual( ['1.100.25.100', '1.100.100.100', '10.100.100.100', '100.100.100.100', '256.100.100.100'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toEqual( ['256.100.100.100', '100.100.100.100', '10.100.100.100', '1.100.100.100', '1.100.25.100'] );
+
+	});
+	it('should sort Tb, Gb, Mb, Kb memory values', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 6, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['2Gb', '4Kb', '1.23TB', '2.73kb', '0.4Tb'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toEqual( ['2.73kb', '4Kb', '2Gb', '0.4Tb', '1.23TB'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toEqual( ['1.23TB', '0.4Tb', '2Gb', '4Kb', '2.73kb'] );
+
+	});
+
+	it('should sort floats', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 7, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['0.01', '10e5', '-1e3', '0.443e-12', '0.1e-99'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toEqual( ['-1e3', '0.1e-99', '0.443e-12', '0.01', '10e5'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toEqual( ['10e5', '0.01', '0.443e-12', '0.1e-99', '-1e3' ] );
+
+	});
+
+	it('should sort strings', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 1, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['zappy', 'happy', 'pappy', 'dappy', 'pappy'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toEqual( ['dappy', 'happy', 'pappy', 'pappy', 'zappy'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toEqual( ['zappy', 'pappy', 'pappy', 'happy', 'dappy'] );
+
+	});
+
+	it('should sort with "sortvalue" attributes', function(){
+
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 3, data=[];
+		var rows = myTable.rows;
+		rows[1].cells[column].setAttribute('jspwiki:sortvalue',0);
+		rows[2].cells[column].setAttribute('jspwiki:sortvalue',1);
+		rows[3].cells[column].setAttribute('jspwiki:sortvalue',2);
+		rows[4].cells[column].setAttribute('jspwiki:sortvalue',3);
+		rows[5].cells[column].setAttribute('jspwiki:sortvalue',4);
+
+
+		data = getcol(column);
+		expect(data).toEqual( ['5', '19', '06', '199', '017'] );
+
+		tt.sort(column); //ascending
+		data = getcol(column);
+		expect(data).toNotEqual( ['5', '06', '017', '19', '199'] );
+		expect(data).toEqual( ['5', '19', '06', '199', '017'] );
+
+		tt.sort(column); //descending
+		data = getcol(column);
+		expect(data).toNotEqual( ['199', '19', '017', '06', '5' ] );
+		expect(data).toEqual( ['017', '199', '06', '19', '5'] );
+
+	});
+
+	it('should filter a columns', function(){
+
+		var tt = new TableAdds(myTable, {filter:true});
+		var column = 3, data=[];
+
+		data = getcol(column);
+		expect(data).toEqual( ['5', '19', '06', '199', '017'] );
+
+		tt.filter(column, 6);
+		data = getcol(column);
+		expect(data).toEqual( ['06'] );
+
+
+	});
+
+	it('should filter a columns with more then 1 unique element', function(){
+
+		var tt = new TableAdds(myTable, {filter:true});
+		var tt = new TableAdds(myTable, {sort:true});
+		var column = 1, data=[];
+
+
+		data = getcol(column);
+		expect(data).toEqual( ['zappy', 'happy', 'pappy', 'dappy', 'pappy'] );
+
+		tt.filter(column, 'pappy');
+		data = getcol(column);
+		expect(data).toEqual( ['pappy','pappy'] );
+
+		data = getcol(0);
+		expect(data).toEqual( ['book3','book5'] );
+	});
+
+	it('should combine sorting and filtering to a table', function(){
+
+		var t1 = new TableAdds(myTable, {filter:true});
+
+		expect(t1.filters).toNotEqual(undefined);
+		expect(t1.sorted).toEqual(undefined);
+
+		var t2 = new TableAdds(myTable, {sort:true});
+		expect(t1).toEqual( t2 );
+		expect(t1.filters).toNotEqual(undefined);
+		expect(t1.sorted).toBeTruthy();
+
+		var column = 1, data=[];
+		data = getcol(column);
+		expect(data).toEqual( ['zappy', 'happy', 'pappy', 'dappy', 'pappy'] );
+
+		t1.sort(column);
+		t1.sort(column);
+		data = getcol(column);
+		expect(data).toEqual( ['zappy', 'pappy', 'pappy', 'happy', 'dappy'] );
+
+		t1.filter(column, 'pappy');
+		data = getcol(column);
+		expect(data).toEqual( ['pappy','pappy'] );
+
+		data = getcol(0);
+		expect(data).toEqual( ['book5','book3'] );
+
+
+	});
+
+	it('should combine sorting, filtering and zebra-stripes to a table', function(){
+
+		var t1 = new TableAdds(myTable, {filter:true, sort:true, zebra:['red'] });
+
+		expect(t1.filters).toNotEqual(undefined);
+		expect(t1.sorted).toBeTruthy();
+		expect(t1.zebra).toNotEqual(undefined);
+
+		var column = 1, data=[];
+		data = getcol(column);
+		expect(data).toEqual( ['zappy', 'happy', 'pappy', 'dappy', 'pappy'] );
+
+		t1.sort(column);
+		t1.sort(column);
+		data = getcol(column);
+		expect(data).toEqual( ['zappy', 'pappy', 'pappy', 'happy', 'dappy'] );
+
+		t1.filter(column, 'pappy');
+		data = getcol(column);
+		expect(data).toEqual( ['pappy','pappy'] );
+		data = getcol(0);
+		expect(data).toEqual( ['book5','book3'] );
+
+		var rows = myTable.rows;
+		expect( rows[0].getStyle('background-color') ).toEqual('transparent');
+		expect( rows[2].getStyle('background-color') ).toEqual('transparent');
+		expect( rows[3].getStyle('background-color') ).toEqual('#ff0000');
+
+	});
+
+});
+
+describe('Categories class',function(){
+});
+
+describe('HighlightWord class',function(){
+});
+
+

Added: incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-edit.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-edit.js?rev=819134&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-edit.js (added)
+++ incubator/jspwiki/trunk/tests/javascript/spec-jspwiki-edit.js Sat Sep 26 13:22:31 2009
@@ -0,0 +1,773 @@
+/*
+Script: spec-jspwiki-edit.js
+
+License:
+	http://www.apache.org/licenses/LICENSE-2.0
+*/
+
+
+/*
+Script: snipeditorSpecs.js
+	Test following classes: WikiEditor, SnipEditor, Textarea, UndoRedo
+
+License:
+	xxx
+
+*/
+
+
+/*
+Native: StubEvent
+	Mockup Event object to allow simulation of keyboard and mouse events
+	(based on mootools v1.2.3)
+*/
+var StubEvent = new Native({
+
+	name: 'Event',
+
+	initialize: function(options){
+		return $extend(this, {
+			event: options.event||{},
+			type: options.type,
+
+			page: options.page,
+			client: options.client,
+			rightClick: options.rightClick,
+
+			wheel: options.wheel,
+
+			relatedTarget: options.relatedTarget,
+			target: options.target,
+
+			code: options.code,
+			key: options.key,
+
+			shift: options.shift,
+			control: options.control,
+			alt: options.alt,
+			meta: options.meta
+		});
+	}
+});
+
+StubEvent.Keys = Event.Keys;
+
+StubEvent.implement({
+	setKey: function(keystroke,shift){
+		this.type='keypress';
+		this.shift=shift||false,
+		this.code=keystroke.charCodeAt(0);
+		this.key=keystroke;
+		return this;
+	},
+	stop: function(){ return this; },
+	stopPropagation: function(){ return this; },
+	preventDefault: function(){ return this; }
+});
+
+
+
+describe('UndoRedo Class',function(){
+
+	var WORK, UndoElement, RedoElement, UNDOREDO;
+
+	beforeEach( function(){
+		WORK = {
+			state : 'testing',
+			getState: function(){ return this.state; },
+			putState: function(obj){ this.state = obj; }
+		}
+
+		$$('body')[0].adopt( UndoElement = new Element('a'), RedoElement = new Element('a') );
+
+		UNDOREDO = new UndoRedo(WORK, {redo:RedoElement, undo:UndoElement});
+
+	});
+
+	afterEach( function(){
+		WORK = null, UNDOREDO = null;
+		UndoElement.dispose();
+		RedoElement.dispose();
+	});
+
+	it('should initialize', function(){
+
+		expect(UNDOREDO.obj).toEqual(WORK);
+		expect(UNDOREDO.options.maxundo).toEqual(40);
+		expect(UNDOREDO.redo).toEqual([]);
+		expect(UNDOREDO.undo).toEqual([]);
+		expect(UNDOREDO.undoEL).toNotEqual(null);
+		expect(UNDOREDO.redoEL).toEqual(RedoElement);
+
+		UNDOREDO = new UndoRedo( WORK ,{maxundo:3});
+
+		expect(UNDOREDO.obj).toEqual(WORK);
+		expect(UNDOREDO.options.maxundo).toEqual(3);
+
+	});
+
+	it('should undo a single element', function(){
+
+		UNDOREDO.onChange();
+		WORK.state = "changed";
+
+		expect(UNDOREDO.undo).toEqual(['testing']);
+		expect(WORK.state).toEqual('changed');
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeTruthy();
+
+		UNDOREDO.onUndo();
+
+		expect(WORK.state).toEqual('testing');
+		expect(UNDOREDO.undo).toEqual([]);
+		expect(UNDOREDO.redo).toEqual(['changed']);
+		expect(UndoElement.hasClass('disabled')).toBeTruthy();
+		expect(RedoElement.hasClass('disabled')).toBeFalsy();
+
+	});
+
+	it('should undo multiple elements', function(){
+
+		UNDOREDO.onChange();
+		WORK.state = "changed1";
+		UNDOREDO.onChange();
+		WORK.state = "changed2";
+
+		expect(UNDOREDO.undo).toEqual(['testing','changed1']);
+		expect(WORK.state).toEqual('changed2');
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeTruthy();
+
+		UNDOREDO.onUndo();
+
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeFalsy();
+
+
+		UNDOREDO.onUndo();
+
+		expect(WORK.state).toEqual('testing');
+		expect(UNDOREDO.undo).toEqual([]);
+		expect(UNDOREDO.redo).toEqual(['changed2','changed1']);
+		expect(UndoElement.hasClass('disabled')).toBeTruthy();
+		expect(RedoElement.hasClass('disabled')).toBeFalsy();
+	});
+
+	it('should undo maximum "maxundo" elements', function(){
+
+		UNDOREDO = new UndoRedo( WORK ,{maxundo:3});
+
+		UNDOREDO.onChange();
+		WORK.state = "changed1";
+
+		expect(UNDOREDO.undo).toEqual(['testing']);
+
+		UNDOREDO.onChange();
+		WORK.state = "changed2";
+
+		expect(UNDOREDO.undo).toEqual(['testing','changed1']);
+
+		UNDOREDO.onChange();
+		WORK.state = "changed3";
+
+		expect(UNDOREDO.undo).toEqual(['testing','changed1','changed2']);
+
+		UNDOREDO.onChange();
+		WORK.state = "changed4";
+
+		expect(UNDOREDO.undo).toEqual(['changed1','changed2','changed3']);
+		expect(WORK.state).toEqual('changed4');
+
+		UNDOREDO.onUndo();
+		UNDOREDO.onUndo();
+		UNDOREDO.onUndo();
+		UNDOREDO.onUndo();
+
+		expect(WORK.state).toEqual('changed1');
+		expect(UNDOREDO.undo).toEqual([]);
+		expect(UNDOREDO.redo).toEqual(['changed4','changed3','changed2']);
+	});
+
+	it('should redo an element', function(){
+
+		UNDOREDO.onChange();
+		WORK.state = "changed1";
+		UNDOREDO.onChange();
+		WORK.state = "changed2";
+
+		expect(UNDOREDO.undo).toEqual(['testing','changed1']);
+		expect(WORK.state).toEqual('changed2');
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeTruthy();
+
+		UNDOREDO.onUndo();
+
+		expect(WORK.state).toEqual('changed1');
+		expect(UNDOREDO.undo).toEqual(['testing']);
+		expect(UNDOREDO.redo).toEqual(['changed2']);
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeFalsy();
+
+		UNDOREDO.onRedo();
+
+		expect(WORK.state).toEqual('changed2');
+		expect(UNDOREDO.undo).toEqual(['testing','changed1']);
+		expect(UNDOREDO.redo).toEqual([]);
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeTruthy();
+
+	});
+
+	it('should clear the redo stack when a new undo element is pushed', function(){
+
+		UNDOREDO.onChange();
+		WORK.state = "changed1";
+		UNDOREDO.onChange();
+		WORK.state = "changed2";
+
+		expect(UNDOREDO.undo).toEqual(['testing','changed1']);
+		expect(WORK.state).toEqual('changed2');
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeTruthy();
+
+		UNDOREDO.onUndo();
+
+		expect(WORK.state).toEqual('changed1');
+		expect(UNDOREDO.undo).toEqual(['testing']);
+		expect(UNDOREDO.redo).toEqual(['changed2']);
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeFalsy();
+
+		//no make a new change, this should clear the redo stack
+		WORK.state = "changed3";
+		UNDOREDO.onChange();
+
+		expect(WORK.state).toEqual('changed3');
+		expect(UNDOREDO.undo).toEqual(['testing','changed3']);
+		expect(UNDOREDO.redo).toEqual([]);
+		expect(UndoElement.hasClass('disabled')).toBeFalsy();
+		expect(RedoElement.hasClass('disabled')).toBeTruthy();
+
+	});
+
+
+});
+
+describe('Textarea Class', function(){
+
+	beforeEach( function() {
+		$$('body')[0].adopt( ta = new Element('textarea') );
+		ta.value = 'Example text';
+
+		txta = new Textarea( ta );
+	});
+
+	afterEach(function(){
+		ta.dispose();
+	});
+
+	it('should initialize Textarea class', function(){
+		expect(txta.getValue()).toEqual('Example text');
+	});
+
+	it('should set the caret', function(){
+		txta.setSelectionRange(2);
+		expect(txta.getSelection()).toEqual('');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:2,thin:true})
+	});
+
+	it('should set a selection range', function(){
+		txta.setSelectionRange(2,4);
+		expect(txta.getSelection()).toEqual('am');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:4,thin:false})
+	});
+
+	it('should set a selection range with a \\n included', function(){
+		ta.value = 'Example\ntext';
+		txta.setSelectionRange(6,9);
+		expect(txta.getSelection()).toEqual('e\nt');
+		expect(txta.getSelectionRange()).toEqual({start:6,end:9,thin:false})
+	});
+
+	it('should read a fragment from start to caret', function(){
+		txta.setSelectionRange(2);
+		expect(txta.getFromStart()).toEqual('Ex');
+	});
+
+	it('should read a fragment from caret till end', function(){
+		txta.setSelectionRange(2,4);
+		expect(txta.getTillEnd()).toEqual('ple text');
+	});
+
+	it('should replace the selection with another fragment', function(){
+		txta.setSelectionRange(2,4);
+		txta.setSelection('New Selection ');
+		expect(txta.getValue()).toEqual('ExNew Selection ple text');
+		expect(txta.getSelection()).toEqual('New Selection ');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:16,thin:false})
+	});
+
+	it('should replace the selection with another fragment with a \\n', function(){
+		txta.setSelectionRange(2,4);
+		txta.setSelection('New\nSelection ');
+		expect(txta.getValue()).toEqual('ExNew\nSelection ple text');
+		expect(txta.getSelection()).toEqual('New\nSelection ');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:16,thin:false})
+	});
+
+	it('should replace the selection with a \\n with another fragment', function(){
+		ta.value = 'Example\ntext';
+		txta.setSelectionRange(6,9);
+		txta.setSelection('New Selection ');
+		expect(txta.getValue()).toEqual('ExamplNew Selection ext');
+		expect(txta.getSelection()).toEqual('New Selection ');
+		expect(txta.getSelectionRange()).toEqual({start:6,end:20,thin:false})
+	});
+
+	it('should insert a fragment at the caret', function(){
+		txta.setSelectionRange(2);
+		txta.setSelection('New Selection ');
+		expect(txta.getValue()).toEqual('ExNew Selection ample text');
+		expect(txta.getSelection()).toEqual('New Selection ');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:16,thin:false})
+	});
+
+	it('should insert a fragment after the selection', function(){
+		txta.setSelectionRange(2,4);
+		txta.insertAfter('Inserted Stuff ')
+		expect(txta.getValue()).toEqual('ExInserted Stuff ple text');
+		expect(txta.getSelection()).toEqual('');
+		expect(txta.getSelectionRange()).toEqual({start:17,end:17,thin:true})
+	});
+
+	it('should insert a set of fragments after the caret', function(){
+		txta.setSelectionRange(2);
+		txta.insertAfter('Inserted ','Stuff ')
+		expect(txta.getValue()).toEqual('ExInserted Stuff ample text');
+		expect(txta.getSelectionRange()).toEqual({start:17,end:17,thin:true})
+	});
+
+	it('should check if caret is at start of line', function(){
+		ta.value = 'Example\ntext';
+		txta.setSelectionRange(0);
+		expect(txta.isCaretAtStartOfLine()).toBeTruthy();
+		txta.setSelectionRange(2);
+		expect(txta.isCaretAtStartOfLine()).toBeFalsy();
+		txta.setSelectionRange(8);
+		expect(txta.isCaretAtStartOfLine()).toBeTruthy();
+		txta.setSelectionRange(9);
+		expect(txta.isCaretAtStartOfLine()).toBeFalsy();
+		txta.setSelectionRange(100);
+		expect(txta.isCaretAtStartOfLine()).toBeFalsy();
+	});
+
+});
+
+describe('Edit: SnipEditor Class', function(){
+
+	var KEY, TAB, main, next, snipe, txta, ta;
+
+	beforeEach( function(){
+
+		KEY = new StubEvent({'type':'keypress', 'shift':false}).setKey('a');
+		TAB = new StubEvent({ 'type':'keydown', 'shift':false, code:9, key:'tab' });
+
+		//initialise body with basic DOM elements to test the snip-editor
+		document.body
+			.adopt( main = new Element('textarea').set('html','Example text') )
+			.adopt( next = new Element('input') );
+		main.set('html','Example text');
+		next.value = "no-focus";
+
+		snipe = new SnipEditor( main, {
+			tabsnips: WikiEdit.tabSnippets,
+			directsnips: WikiEdit.directSnippets,
+			//buttons: $$('a.tool'),
+			next:next,
+			//suggest:$('suggestionMenu')
+	  	});
+
+	  	txta = snipe.get('textarea');
+	  	ta = txta.toElement();
+
+		snipe.options.findForm = {
+			findInput: {value: ''},
+			replaceInput: {value: ''},
+			isRegExp: {checked:false},
+			isMatchCase: {checked:false},
+			isReplaceGlobal: {checked:true},
+			msgNoMatchFound: function(){ }
+		};
+
+
+	});
+
+	afterEach(function(){
+		main.dispose(), ta.dispose(), next.dispose(), snipe=null, txta=null;
+	});
+
+	it('should initialize the SnipEditor class', function(){
+		expect(main.value).toEqual('Example text');
+		expect(ta.value).toEqual('Example text');
+		expect(txta.getValue()).toEqual('Example text');
+	});
+
+	//Special keys
+	it('shift-enter should jump to next form element', function(){
+		next.addEvent('focus',function(){ this.value="has-focus"});
+		txta.setSelectionRange(2);
+
+		expect(next.get('value')).toEqual('no-focus');
+
+		var SHIFTENTER = new StubEvent({ 'type':'keydown', 'shift':true, code:13, key:'enter' });
+
+		snipe.onKeystroke( SHIFTENTER );
+
+		//expect(txta.getValue()).toEqual('Example text');
+		//expect(txta.getSelectionRange()).toEqual({start:2,end:2,thin:true});
+		expect(next.get('value')).toEqual('has-focus');
+	});
+
+	//auto indentation on TAB
+	it('tab key should insert tab-spaces at the caret', function(){
+		txta.setSelectionRange(2);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Ex    ample text');
+		expect(txta.getSelectionRange()).toEqual({start:6,end:6,thin:true})
+	});
+
+	it('selected text + tab key should replace the selection with tab-spaces', function(){
+		txta.setSelectionRange(2,4);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Ex    ple text');
+		expect(txta.getSelectionRange()).toEqual({start:6,end:6,thin:true})
+	});
+
+	it('multi-line selection + tab key should insert tab-spaces at each line', function(){
+		txta.toElement().value="Example\ntext";
+		txta.setSelectionRange(0,12);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('    Example\n    text');
+		expect(txta.getSelectionRange()).toEqual({start:0,end:20,thin:false})
+	});
+
+	it('shift-tab key should remove tab-spaces to the left', function(){
+		txta.toElement().value="Ex    ample text";
+		txta.setSelectionRange(6);
+
+		TAB.shift=true;
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Example text');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:2,thin:true})
+	});
+
+	it('multi-line selection + shift-tab key should remove the inserted tab-spaces at each line', function(){
+		txta.toElement().value="    Example\n    text";
+		txta.setSelectionRange(0,20);
+		TAB.shift=true;
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Example\ntext');
+		expect(txta.getSelectionRange()).toEqual({start:0,end:12,thin:false})
+	});
+
+	it('del key should remove tab-spaces to the right', function(){
+		txta.toElement().value="Ex    ample text";
+		txta.setSelectionRange(2);
+		var DEL = new StubEvent({ 'type':'keydown', 'shift':false, code:46, key:'delete' });
+		snipe.onKeystroke( DEL );
+
+		expect(txta.getValue()).toEqual('Example text');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:2,thin:true});
+	});
+
+
+	//direct snippets
+	it('Direct snippets, should insert a closing-bracket after the caret', function(){
+		txta.setSelectionRange(2);
+		snipe.onKeystroke( KEY.setKey('(') );
+
+		expect(txta.getValue()).toEqual('Ex()ample text');
+		expect(txta.getSelectionRange()).toEqual({start:3,end:3,thin:true})	;
+	});
+
+	it('Direct snippets, should insert a bracket around the selection', function(){
+		txta.setSelectionRange(2,4);
+		snipe.onKeystroke( KEY.setKey('[') );
+
+		expect(txta.getValue()).toEqual('Ex[am]ple text');
+		expect(txta.getSelectionRange()).toEqual({start:3,end:5,thin:false});
+	});
+
+	it('should not expand direct snippets when deactivated', function(){
+		snipe.options.directsnips = {};
+		txta.setSelectionRange(2);
+
+		snipe.onKeystroke( KEY.setKey('a'));
+
+		//Warning: default key-stroke behaviour is not correct during Spec tests
+		// because the way it is simulated.
+		//  Normally we would expect 'Ex[ample text'
+		expect(txta.getValue()).toEqual('Example text');
+		expect(txta.getSelectionRange()).toEqual({start:2,end:2,thin:true});
+	});
+
+	//Tab snippet, without parameters
+	it('Tab snippet - no parameters: should remove the leading \\n at the start of the first line of the textarea', function(){
+		snipe.options.tabsnips["toc"] = "\n[\\{TableOfContents }]\n";
+		txta.toElement().value="tocExample text";
+		txta.setSelectionRange(3);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('[{TableOfContents }]\nExample text');
+		expect(txta.getSelectionRange()).toEqual({start:21,end:21,thin:true});
+
+	});
+	it('Tab snippet - no parameters: should remove the leading \\n at the start of the nth line the textarea', function(){
+		snipe.options.tabsnips["toc"] = "\n[\\{TableOfContents }]\n";
+		txta.toElement().value="Example\ntoc text";
+		txta.setSelectionRange(11);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Example\n[{TableOfContents }]\n text');
+		expect(txta.getSelectionRange()).toEqual({start:29,end:29,thin:true})
+	});
+	it('Tab snippet - no parameters: should keep a leading \\n in the middle of a line of the textarea', function(){
+		snipe.options.tabsnips["toc"] = "\n[\\{TableOfContents }]\n";
+		txta.toElement().value="Example toc text";
+		txta.setSelectionRange(11);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Example \n[{TableOfContents }]\n text');
+		expect(txta.getSelectionRange()).toEqual({start:30,end:30,thin:true})
+	});
+
+	it('Tab snippet: should only allow a tab snippet when in scope', function(){
+		snipe.options.tabsnips["toc"] = {
+			snippet:"\n[\\{TableOfContents }]\n",
+			scope:{
+				"test": "endtest",
+				"<p": "</p>"
+			}
+		}
+
+		//in scope
+		txta.toElement().value="Example <p>toc</p> text";
+		txta.setSelectionRange(14);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual("Example <p>\n[{TableOfContents }]\n</p> text");
+		expect(txta.getSelectionRange()).toEqual({start:33,end:33,thin:true})
+
+		//not in scope -- so just expands the tab to 4 spaces
+		txta.toElement().value="Example toc text";
+		txta.setSelectionRange(11);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual("Example toc     text");
+		expect(txta.getSelectionRange()).toEqual({start:15,end:15,thin:true})
+
+	});
+
+	it('Tab snippet: should not expand tab snippets when deactivated', function(){
+		snipe.options.tabsnips = {}
+		txta.toElement().value="Example toc text";
+		txta.setSelectionRange(11);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Example toc     text');
+		expect(txta.getSelectionRange()).toEqual({start:15,end:15,thin:true})
+	});
+
+	it('Tab snippet: should allow to undo the inserted tab snippet', function(){
+		snipe.options.tabsnips["toc"] = "\n[\\{TableOfContents }]\n";
+		txta.toElement().value="Example toc text";
+		txta.setSelectionRange(11);
+		snipe.onKeystroke( TAB );
+
+		expect(txta.getValue()).toEqual('Example \n[{TableOfContents }]\n text');
+		expect(txta.getSelectionRange()).toEqual({start:30,end:30,thin:true});
+
+		snipe.undoredo.onUndo();
+
+		expect(txta.getValue()).toEqual('Example toc text');
+		expect(txta.getSelectionRange()).toEqual({start:11,end:11,thin:true});
+
+		snipe.undoredo.onRedo();
+
+		expect(txta.getValue()).toEqual('Example \n[{TableOfContents }]\n text');
+		expect(txta.getSelectionRange()).toEqual({start:30,end:30,thin:true});
+	});
+
+	//Tab snippet, with a parameter
+	it('Tab snippet with parameter: should select the first parameter', function(){
+
+	});
+
+	it('Tab snippet with parameter: should allow to tab through all subsequent parameters', function(){
+
+	});
+
+	it('Tab snippet with parameter: should remove the active snippet when pressing esc/up/down', function(){
+
+	});
+
+	it('Tab snippet with parameter and parameter dialog: should display the parameter dialog', function(){
+
+	});
+
+	it('Tab snippet with font parameter: should display the font dialog', function(){
+
+	});
+
+	it('Tab snippet with color parameter: should display the color dialog', function(){
+
+	});
+
+	it('Tab snippet with special-charater parameter: should display the chars dialog', function(){
+
+	});
+
+	//Button snippets
+	it('Button snippet, should insert snippet text without parameters', function(){
+	});
+
+	it('Button snippet, should replace selected text by snippet without parameters', function(){
+	});
+
+	it('Button snippet, should insert snippet and tab through parameters without parameter dialog', function(){
+	});
+
+	it('Button snippet, should insert snippet and select first parameter with parameter dialog', function(){
+	});
+
+	it('Button snippet, should insert snippet and replace first free parameter with seleted text', function(){
+		//font snippet
+	});
+
+
+	//find and replace
+	it('should replace all occurences of a string', function(){
+
+		txta.toElement().value='Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.';
+		snipe.options.findForm.findInput.value='ipsum';
+		snipe.options.findForm.replaceInput.value='IPSUM'
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('Lorem IPSUM dolor sit amet. Lorem IPSUM dolor sit amet.');
+	});
+
+	it('should undo a replace operation', function(){
+
+		txta.toElement().value='Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.';
+		snipe.options.findForm.findInput.value='ipsum';
+		snipe.options.findForm.replaceInput.value='IPSUM'
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('Lorem IPSUM dolor sit amet. Lorem IPSUM dolor sit amet.');
+
+		snipe.undoredo.onUndo();
+
+		expect(txta.getValue()).toEqual('Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.');
+
+		snipe.undoredo.onRedo();
+
+		expect(txta.getValue()).toEqual('Lorem IPSUM dolor sit amet. Lorem IPSUM dolor sit amet.');
+
+	});
+
+	it('should return no-match found when replacing a string', function(){
+
+		txta.toElement().value='Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.';
+		snipe.options.findForm.findInput.value='cant find this string';
+		snipe.options.findForm.replaceInput.value='oops';
+		var error = '';
+		snipe.options.findForm.msgNoMatchFound=function(){ error = 'no match';}
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.');
+		expect(error).toEqual('no match');
+	});
+
+	it('should replace all occurences of a string case sensitive', function(){
+
+		txta.toElement().value='Lorem ipsum dolor sit amet; lorem ipsum dolor sit amet.';
+		snipe.options.findForm.findInput.value='Lorem';
+		snipe.options.findForm.replaceInput.value='LOREM'
+		snipe.options.findForm.isMatchCase.checked=true;
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('LOREM ipsum dolor sit amet; lorem ipsum dolor sit amet.');
+	});
+
+	it('should replace all occurences of a string with a regular expression', function(){
+
+		txta.toElement().value='Lorem ipsum dolor sit amet. LOREM iPSUm dolor sit amet.';
+		snipe.options.findForm.findInput.value='i([psu]+)m';
+		snipe.options.findForm.replaceInput.value='I$1M'
+		snipe.options.findForm.isRegExp.checked=true;
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('Lorem IpsuM dolor sit amet. LOREM IPSUM dolor sit amet.');
+
+		//double $ in replace string acts as a single $
+		snipe.options.findForm.findInput.value='L([orem]+)m';
+		snipe.options.findForm.replaceInput.value='LL$$$1MM'
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('LL$oreMM IpsuM dolor sit amet. LL$OREMM IPSUM dolor sit amet.');
+	});
+
+	it('should replace the first occurences of a string', function(){
+
+		txta.toElement().value='Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.';
+		snipe.options.findForm.findInput.value='ipsum';
+		snipe.options.findForm.replaceInput.value='IPSUM'
+		snipe.options.findForm.isReplaceGlobal.checked=false;
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('Lorem IPSUM dolor sit amet. Lorem ipsum dolor sit amet.');
+	});
+
+	it('should replace all occurences of a string in a textarea selection', function(){
+
+		txta.toElement().value='Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.';
+		txta.setSelectionRange(6,27)
+		snipe.options.findForm.findInput.value='i';
+		snipe.options.findForm.replaceInput.value='III'
+
+		expect(txta.getSelection()).toEqual('ipsum dolor sit amet.');
+
+		snipe.onFindAndReplace();
+
+		expect(txta.getValue()).toEqual('Lorem IIIpsum dolor sIIIt amet. Lorem ipsum dolor sit amet.');
+		expect(txta.getSelection()).toEqual('IIIpsum dolor sIIIt amet.');
+	});
+
+
+});
+
+//todo
+describe('Edit: Wiki Editor Class', function(){
+
+	//test configuration dialog
+
+	//setting/reseting of smartpair and tabcompletion preferences
+
+	//test cookie get/set
+
+});
+
+
+