Skip to content
Browse files

initial commit.

  • Loading branch information...
0 parents commit 3eda83804e739b4ba62813b9304a70bf0eb911a0 @garyharan committed
Showing with 5,603 additions and 0 deletions.
  1. +92 −0 jquery.caretPosition.js
  2. +64 −0 jquery_caretPosition_test.html
  3. +4,376 −0 lib/jquery.js
  4. +1,017 −0 lib/jsunittest.js
  5. +54 −0 lib/unittest.css
92 jquery.caretPosition.js
@@ -0,0 +1,92 @@
+(function($){
+ $.fn.insertAtCaret = function(text, opts) {
+ var element = $(this).get(0);
+
+ if (document.selection) {
+ element.focus();
+ var orig = element.value.replace(/\r\n/g, "\n");
+ var range = document.selection.createRange();
+
+ if (range.parentElement() != element) {
+ return false;
+ }
+
+ range.text = text;
+
+ var actual = tmp = element.value.replace(/\r\n/g, "\n");
+
+ for (var diff = 0; diff < orig.length; diff++) {
+ if (orig.charAt(diff) != actual.charAt(diff)) break;
+ }
+
+ for (var index = 0, start = 0; tmp.match(text) && (tmp = tmp.replace(text, "")) && index <= diff; index = start + text.length ) {
+ start = actual.indexOf(text, index);
+ }
+ } else if (element.selectionStart) {
+ var start = element.selectionStart;
+ var end = element.selectionEnd;
+
+ element.value = element.value.substr(0, start) + text + element.value.substr(end, element.value.length);
+ }
+
+ if (start != null) {
+ setCaretTo(element, start + text.length);
+ } else {
+ element.value += text;
+ }
+
+ return this;
+ }
+
+ $.fn.setCaretPosition = function(start, end) {
+ var element = $(this).get(0);
+ element.focus();
+ setCaretTo(element, start, end);
+ return this;
+ }
+
+
+ $.fn.getCaretPosition = function() {
+ var element = $(this).get(0);
+ $(element).focus();
+ return getCaretPosition(element);
+ }
+
+ // privates
+ function setCaretTo(element, start, end) {
+ if(element.createTextRange) {
+ var range = element.createTextRange();
+ range.moveStart('character', start);
+ range.moveEnd('character', (end || start));
+ range.select();
+ } else if(element.selectionStart) {
+ element.focus();
+ element.setSelectionRange(start, (end || start));
+ }
+ }
+
+ function getCaretPosition(element) {
+ if (typeof element.selectionStart == 'number'){
+ return element.selectionStart;
+ } else if (document.selection) {
+ var range = document.selection.createRange();
+ var rangeLength = range.text.length;
+ range.moveStart('character', -element.value.length);
+ return range.text.length - rangeLength;
+ }
+ }
+
+ // DANGER... this will does not necessarily return text selected within the element you call.
+ $.fn.getSelectedText = function() {
+ var element = $(this).get(0);
+
+ // workaround for firefox because window.getSelection does not work inside inputs
+ if (typeof element.selectionStart == 'number') {
+ return $(element).val().substr(element.selectionStart, element.selectionEnd - element.selectionStart);
+ } else if (document.getSelection) {
+ return document.getSelection();
+ } else if (window.getSelection) {
+ return window.getSelection();
+ }
+ }
+})(jQuery);
64 jquery_caretPosition_test.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>JavaScript unit test file</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script src="lib/jsunittest.js" type="text/javascript"></script>
+ <script src="lib/jquery.js" type="text/javascript"></script>
+
+ <!-- TODO: REPLACE "example_library_to_test" throughout file with name of file to test -->
+ <script src="jquery.caretPosition.js" type="text/javascript"></script>
+
+ <link rel="stylesheet" href="lib/unittest.css" type="text/css" />
+</head>
+<body>
+
+<div id="content">
+
+ <div id="header">
+ <h1>JavaScript unit test file</h1>
+ <p>
+ This file tests <strong>jquery.caretPosition.js</strong>.
+ </p>
+ </div>
+
+ <!-- Log output (one per Runner, via {testLog: "testlog"} option)-->
+ <div id="testlog"></div>
+
+ <!-- Put sample/test html here -->
+ <div id="sample">
+ </div>
+</div>
+
+<script type="text/javascript">
+// <![CDATA[
+ new Test.Unit.Runner({
+ setup: function(){
+ if ($('#test_input').length == 0){
+ $('<input/>').attr('id', 'test_input').appendTo(document.body).focus().css({'position': 'absolute', 'top': '-1000px'});
+ }
+ },
+
+ testGetAndSetCaretPosition: function(){ with(this) {
+ assertEqual(5, $('#test_input').val('emergency broadcast system').setCaretPosition(5).getCaretPosition());
+ assertEqual(5, $('#test_input').val('emergency broadcast system').setCaretPosition(5, 8).getCaretPosition());
+ }},
+
+ testInsertAtCaret: function() { with(this) {
+ assertEqual('hello', $('#test_input').val('helo').setCaretPosition(2).insertAtCaret('l').val());
+ }},
+
+ testChaining: function() { with(this) {
+ assertEqual('this is a test of the emergency broadcast system',
+ $('#test_input').val('this of the emergency broadcast system').setCaretPosition(5).insertAtCaret('is a test ').setCaretPosition(10, 14).val());
+ }},
+
+ testGetSelectedText: function() { with(this){
+ assertEqual('test', $('#test_input').val('this is a test of that').setCaretPosition(10, 14).getSelectedText());
+ }}
+ });
+// ]]>
+</script>
+</body>
+</html>
4,376 lib/jquery.js
4,376 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
1,017 lib/jsunittest.js
@@ -0,0 +1,1017 @@
+/* Jsunittest, version 0.7.3
+ * (c) 2008 Dr Nic Williams
+ *
+ * Jsunittest is freely distributable under
+ * the terms of an MIT-style license.
+ * For details, see the web site: http://jsunittest.rubyforge.org
+ *
+ *--------------------------------------------------------------------------*/
+
+var JsUnitTest = {
+ Unit: {},
+ inspect: function(object) {
+ try {
+ if (typeof object == "undefined") return 'undefined';
+ if (object === null) return 'null';
+ if (typeof object == "string") {
+ var useDoubleQuotes = arguments[1];
+ var escapedString = this.gsub(object, /[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ };
+ return String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+ $: function(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push(this.$(arguments[i]));
+ return elements;
+ }
+ if (typeof element == "string")
+ element = document.getElementById(element);
+ return element;
+ },
+ gsub: function(source, pattern, replacement) {
+ var result = '', match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += JsUnitTest.String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+ scan: function(source, pattern, iterator) {
+ this.gsub(source, pattern, iterator);
+ return String(source);
+ },
+ escapeHTML: function(data) {
+ return data.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+ },
+ arrayfromargs: function(args) {
+ var myarray = new Array();
+ var i;
+
+ for (i=0;i<args.length;i++)
+ myarray[i] = args[i];
+
+ return myarray;
+ },
+ hashToSortedArray: function(hash) {
+ var results = [];
+ for (key in hash) {
+ results.push([key, hash[key]]);
+ }
+ return results.sort();
+ },
+ flattenArray: function(array) {
+ var results = arguments[1] || [];
+ for (var i=0; i < array.length; i++) {
+ var object = array[i];
+ if (object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object) {
+ this.flattenArray(object, results);
+ } else {
+ results.push(object);
+ }
+ };
+ return results;
+ },
+ selectorMatch: function(expression, element) {
+ var tokens = [];
+ var patterns = {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+ /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ };
+
+ var assertions = {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ };
+ var e = this.expression, ps = patterns, as = assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+ toQueryParams: function(query, separator) {
+ var query = query || window.location.search;
+ var match = query.replace(/^\s+/, '').replace(/\s+$/, '').match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ var hash = {};
+ var parts = match[1].split(separator || '&');
+ for (var i=0; i < parts.length; i++) {
+ var pair = parts[i].split('=');
+ if (pair[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ var object = hash[key];
+ var isArray = object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object
+ if (!isArray) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ };
+ return hash;
+ },
+
+ String: {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ }
+ }
+};
+
+JsUnitTest.gsub.prepareReplacement = function(replacement) {
+ if (typeof replacement == "function") return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+JsUnitTest.Version = '0.7.3';
+
+JsUnitTest.Template = function(template, pattern) {
+ this.template = template; //template.toString();
+ this.pattern = pattern || JsUnitTest.Template.Pattern;
+};
+
+JsUnitTest.Template.prototype.evaluate = function(object) {
+ if (typeof object.toTemplateReplacements == "function")
+ object = object.toTemplateReplacements();
+
+ return JsUnitTest.gsub(this.template, this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = (match[1].indexOf('[]') === 0) ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + JsUnitTest.String.interpret(ctx);
+ });
+}
+
+JsUnitTest.Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+JsUnitTest.Event = {};
+// written by Dean Edwards, 2005
+// with input from Tino Zijdel, Matthias Miller, Diego Perini
+// namespaced by Dr Nic Williams 2008
+
+// http://dean.edwards.name/weblog/2005/10/add-event/
+// http://dean.edwards.name/weblog/2005/10/add-event2/
+JsUnitTest.Event.addEvent = function(element, type, handler) {
+ if (element.addEventListener) {
+ element.addEventListener(type, handler, false);
+ } else {
+ // assign each event handler a unique ID
+ if (!handler.$$guid) handler.$$guid = JsUnitTest.Event.addEvent.guid++;
+ // create a hash table of event types for the element
+ if (!element.events) element.events = {};
+ // create a hash table of event handlers for each element/event pair
+ var handlers = element.events[type];
+ if (!handlers) {
+ handlers = element.events[type] = {};
+ // store the existing event handler (if there is one)
+ if (element["on" + type]) {
+ handlers[0] = element["on" + type];
+ }
+ }
+ // store the event handler in the hash table
+ handlers[handler.$$guid] = handler;
+ // assign a global event handler to do all the work
+ element["on" + type] = this.handleEvent;
+ }
+};
+// a counter used to create unique IDs
+JsUnitTest.Event.addEvent.guid = 1;
+
+JsUnitTest.Event.removeEvent = function(element, type, handler) {
+ if (element.removeEventListener) {
+ element.removeEventListener(type, handler, false);
+ } else {
+ // delete the event handler from the hash table
+ if (element.events && element.events[type]) {
+ delete element.events[type][handler.$$guid];
+ }
+ }
+};
+
+JsUnitTest.Event.handleEvent = function(event) {
+ var returnValue = true;
+ // grab the event object (IE uses a global event object)
+ event = event || JsUnitTest.Event.fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
+ // get a reference to the hash table of event handlers
+ var handlers = this.events[event.type];
+ // execute each event handler
+ for (var i in handlers) {
+ this.$$handleEvent = handlers[i];
+ if (this.$$handleEvent(event) === false) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
+};
+
+JsUnitTest.Event.fixEvent = function(event) {
+ // add W3C standard event methods
+ event.preventDefault = this.fixEvent.preventDefault;
+ event.stopPropagation = this.fixEvent.stopPropagation;
+ return event;
+};
+JsUnitTest.Event.fixEvent.preventDefault = function() {
+ this.returnValue = false;
+};
+JsUnitTest.Event.fixEvent.stopPropagation = function() {
+ this.cancelBubble = true;
+};
+
+JsUnitTest.Unit.Logger = function(element) {
+ this.element = JsUnitTest.$(element);
+ if (this.element) this._createLogTable();
+};
+
+JsUnitTest.Unit.Logger.prototype.start = function(testName) {
+ if (!this.element) return;
+ var tbody = this.element.getElementsByTagName('tbody')[0];
+
+ var tr = document.createElement('tr');
+ var td;
+
+ //testname
+ td = document.createElement('td');
+ td.appendChild(document.createTextNode(testName));
+ tr.appendChild(td)
+
+ tr.appendChild(document.createElement('td'));//status
+ tr.appendChild(document.createElement('td'));//message
+
+ tbody.appendChild(tr);
+};
+
+JsUnitTest.Unit.Logger.prototype.setStatus = function(status) {
+ var logline = this.getLastLogLine();
+ logline.className = status;
+ var statusCell = logline.getElementsByTagName('td')[1];
+ statusCell.appendChild(document.createTextNode(status));
+};
+
+JsUnitTest.Unit.Logger.prototype.finish = function(status, summary) {
+ if (!this.element) return;
+ this.setStatus(status);
+ this.message(summary);
+};
+
+JsUnitTest.Unit.Logger.prototype.message = function(message) {
+ if (!this.element) return;
+ var cell = this.getMessageCell();
+
+ // cell.appendChild(document.createTextNode(this._toHTML(message)));
+ cell.innerHTML = this._toHTML(message);
+};
+
+JsUnitTest.Unit.Logger.prototype.summary = function(summary) {
+ if (!this.element) return;
+ var div = this.element.getElementsByTagName('div')[0];
+ div.innerHTML = this._toHTML(summary);
+};
+
+JsUnitTest.Unit.Logger.prototype.getLastLogLine = function() {
+ var tbody = this.element.getElementsByTagName('tbody')[0];
+ var loglines = tbody.getElementsByTagName('tr');
+ return loglines[loglines.length - 1];
+};
+
+JsUnitTest.Unit.Logger.prototype.getMessageCell = function() {
+ var logline = this.getLastLogLine();
+ return logline.getElementsByTagName('td')[2];
+};
+
+JsUnitTest.Unit.Logger.prototype._createLogTable = function() {
+ var html = '<div class="logsummary">running...</div>' +
+ '<table class="logtable">' +
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+ '<tbody class="loglines"></tbody>' +
+ '</table>';
+ this.element.innerHTML = html;
+};
+
+JsUnitTest.Unit.Logger.prototype.appendActionButtons = function(actions) {
+ // actions = $H(actions);
+ // if (!actions.any()) return;
+ // var div = new Element("div", {className: 'action_buttons'});
+ // actions.inject(div, function(container, action) {
+ // var button = new Element("input").setValue(action.key).observe("click", action.value);
+ // button.type = "button";
+ // return container.insert(button);
+ // });
+ // this.getMessageCell().insert(div);
+};
+
+JsUnitTest.Unit.Logger.prototype._toHTML = function(txt) {
+ return JsUnitTest.escapeHTML(txt).replace(/\n/g,"<br/>");
+};
+JsUnitTest.Unit.MessageTemplate = function(string) {
+ var parts = [];
+ var str = JsUnitTest.scan((string || ''), /(?=[^\\])\?|(?:\\\?|[^\?])+/, function(part) {
+ parts.push(part[0]);
+ });
+ this.parts = parts;
+};
+
+JsUnitTest.Unit.MessageTemplate.prototype.evaluate = function(params) {
+ var results = [];
+ for (var i=0; i < this.parts.length; i++) {
+ var part = this.parts[i];
+ var result = (part == '?') ? JsUnitTest.inspect(params.shift()) : part.replace(/\\\?/, '?');
+ results.push(result);
+ };
+ return results.join('');
+};
+// A generic function for performming AJAX requests
+// It takes one argument, which is an object that contains a set of options
+// All of which are outline in the comments, below
+// From John Resig's book Pro JavaScript Techniques
+// published by Apress, 2006-8
+JsUnitTest.ajax = function( options ) {
+
+ // Load the options object with defaults, if no
+ // values were provided by the user
+ options = {
+ // The type of HTTP Request
+ type: options.type || "POST",
+
+ // The URL the request will be made to
+ url: options.url || "",
+
+ // How long to wait before considering the request to be a timeout
+ timeout: options.timeout || 5000,
+
+ // Functions to call when the request fails, succeeds,
+ // or completes (either fail or succeed)
+ onComplete: options.onComplete || function(){},
+ onError: options.onError || function(){},
+ onSuccess: options.onSuccess || function(){},
+
+ // The data type that'll be returned from the server
+ // the default is simply to determine what data was returned from the
+ // and act accordingly.
+ data: options.data || ""
+ };
+
+ // Create the request object
+ var xml = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
+
+ // Open the asynchronous POST request
+ xml.open(options.type, options.url, true);
+
+ // We're going to wait for a request for 5 seconds, before giving up
+ var timeoutLength = 5000;
+
+ // Keep track of when the request has been succesfully completed
+ var requestDone = false;
+
+ // Initalize a callback which will fire 5 seconds from now, cancelling
+ // the request (if it has not already occurred).
+ setTimeout(function(){
+ requestDone = true;
+ }, timeoutLength);
+
+ // Watch for when the state of the document gets updated
+ xml.onreadystatechange = function(){
+ // Wait until the data is fully loaded,
+ // and make sure that the request hasn't already timed out
+ if ( xml.readyState == 4 && !requestDone ) {
+
+ // Check to see if the request was successful
+ if ( httpSuccess( xml ) ) {
+
+ // Execute the success callback with the data returned from the server
+ options.onSuccess( httpData( xml, options.type ) );
+
+ // Otherwise, an error occurred, so execute the error callback
+ } else {
+ options.onError();
+ }
+
+ // Call the completion callback
+ options.onComplete();
+
+ // Clean up after ourselves, to avoid memory leaks
+ xml = null;
+ }
+ };
+
+ // Establish the connection to the server
+ xml.send(null);
+
+ // Determine the success of the HTTP response
+ function httpSuccess(r) {
+ try {
+ // If no server status is provided, and we're actually
+ // requesting a local file, then it was successful
+ return !r.status && location.protocol == "file:" ||
+
+ // Any status in the 200 range is good
+ ( r.status >= 200 && r.status < 300 ) ||
+
+ // Successful if the document has not been modified
+ r.status == 304 ||
+
+ // Safari returns an empty status if the file has not been modified
+ navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined";
+ } catch(e){}
+
+ // If checking the status failed, then assume that the request failed too
+ return false;
+ }
+
+ // Extract the correct data from the HTTP response
+ function httpData(r,type) {
+ // Get the content-type header
+ var ct = r.getResponseHeader("content-type");
+
+ // If no default type was provided, determine if some
+ // form of XML was returned from the server
+ var data = !type && ct && ct.indexOf("xml") >= 0;
+
+ // Get the XML Document object if XML was returned from
+ // the server, otherwise return the text contents returned by the server
+ data = type == "xml" || data ? r.responseXML : r.responseText;
+
+ // If the specified type is "script", execute the returned text
+ // response as if it was JavaScript
+ if ( type == "script" )
+ eval.call( window, data );
+
+ // Return the response data (either an XML Document or a text string)
+ return data;
+ }
+
+};
+JsUnitTest.Unit.Assertions = {
+ buildMessage: function(message, template) {
+ var args = JsUnitTest.arrayfromargs(arguments).slice(2);
+ return (message ? message + '\n' : '') +
+ new JsUnitTest.Unit.MessageTemplate(template).evaluate(args);
+ },
+
+ flunk: function(message) {
+ this.assertBlock(message || 'Flunked', function() { return false });
+ },
+
+ assertBlock: function(message, block) {
+ try {
+ block.call(this) ? this.pass() : this.fail(message);
+ } catch(e) { this.error(e) }
+ },
+
+ assert: function(expression, message) {
+ message = this.buildMessage(message || 'assert', 'got <?>', expression);
+ this.assertBlock(message, function() { return expression });
+ },
+
+ assertEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected == actual });
+ },
+
+ assertNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected != actual });
+ },
+
+ assertEnumEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEnumEqual', 'expected <?>, actual: <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(expected);
+ var actual_array = JsUnitTest.flattenArray(actual);
+ this.assertBlock(message, function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return false;
+ };
+ return true;
+ }
+ return false;
+ });
+ },
+
+ assertEnumNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEnumNotEqual', '<?> was the same as <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(expected);
+ var actual_array = JsUnitTest.flattenArray(actual);
+ this.assertBlock(message, function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return true;
+ };
+ return false;
+ }
+ return true;
+ });
+ },
+
+ assertHashEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertHashEqual', 'expected <?>, actual: <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(expected));
+ var actual_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(actual));
+ var block = function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return false;
+ };
+ return true;
+ }
+ return false;
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertHashNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertHashNotEqual', '<?> was the same as <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(expected));
+ var actual_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(actual));
+ // from now we recursively zip & compare nested arrays
+ var block = function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return true;
+ };
+ return false;
+ }
+ return true;
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected === actual });
+ },
+
+ assertNotIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected !== actual });
+ },
+
+ assertNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj === null });
+ },
+
+ assertNotNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj !== null });
+ },
+
+ assertUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj == "undefined" });
+ },
+
+ assertNotUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj != "undefined" });
+ },
+
+ assertNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj == null });
+ },
+
+ assertNotNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj != null });
+ },
+
+ assertMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertMatch', 'regex <?> did not match <?>', expected, actual);
+ this.assertBlock(message, function() { return new RegExp(expected).exec(actual) });
+ },
+
+ assertNoMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNoMatch', 'regex <?> matched <?>', expected, actual);
+ this.assertBlock(message, function() { return !(new RegExp(expected).exec(actual)) });
+ },
+
+ assertHasClass: function(element, klass, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertHasClass', '? doesn\'t have class <?>.', element, klass);
+ this.assertBlock(message, function() {
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == klass ||
+ new RegExp("(^|\\s)" + klass + "(\\s|$)").test(elementClassName)));
+ // return !!element.className.match(new RegExp(klass))
+ });
+ },
+
+ assertNotHasClass: function(element, klass, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertNotHasClass', '? does have class <?>.', element, klass);
+ this.assertBlock(message, function() {
+ var elementClassName = element.className;
+ return !(elementClassName.length > 0 && (elementClassName == klass ||
+ new RegExp("(^|\\s)" + klass + "(\\s|$)").test(elementClassName)));
+ });
+ },
+
+ assertHidden: function(element, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertHidden', '? isn\'t hidden.', element);
+ this.assertBlock(message, function() { return !element.style.display || element.style.display == 'none' });
+ },
+
+ assertInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertInstanceOf', '<?> was not an instance of the expected type', actual);
+ this.assertBlock(message, function() { return actual instanceof expected });
+ },
+
+ assertNotInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotInstanceOf', '<?> was an instance of the expected type', actual);
+ this.assertBlock(message, function() { return !(actual instanceof expected) });
+ },
+
+ assertRespondsTo: function(method, obj, message) {
+ message = this.buildMessage(message || 'assertRespondsTo', 'object doesn\'t respond to <?>', method);
+ this.assertBlock(message, function() { return (method in obj && typeof obj[method] == 'function') });
+ },
+
+ assertRaise: function(exceptionName, method, message) {
+ message = this.buildMessage(message || 'assertRaise', '<?> exception expected but none was raised', exceptionName);
+ var block = function() {
+ try {
+ method();
+ return false;
+ } catch(e) {
+ if (e.name == exceptionName) return true;
+ else throw e;
+ }
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertNothingRaised: function(method, message) {
+ try {
+ method();
+ this.assert(true, "Expected nothing to be thrown");
+ } catch(e) {
+ message = this.buildMessage(message || 'assertNothingRaised', '<?> was thrown when nothing was expected.', e);
+ this.flunk(message);
+ }
+ },
+
+ _isVisible: function(element) {
+ element = JsUnitTest.$(element);
+ if(!element.parentNode) return true;
+ this.assertNotNull(element);
+ if(element.style && (element.style.display == 'none'))
+ return false;
+
+ return arguments.callee.call(this, element.parentNode);
+ },
+
+ assertVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not visible.', element);
+ this.assertBlock(message, function() { return this._isVisible(element) });
+ },
+
+ assertNotVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not hidden and didn\'t have a hidden parent either.', element);
+ this.assertBlock(message, function() { return !this._isVisible(element) });
+ },
+
+ assertElementsMatch: function() {
+ var pass = true, expressions = JsUnitTest.arrayfromargs(arguments);
+ var elements = expressions.shift();
+ if (elements.length != expressions.length) {
+ message = this.buildMessage('assertElementsMatch', 'size mismatch: ? elements, ? expressions (?).', elements.length, expressions.length, expressions);
+ this.flunk(message);
+ pass = false;
+ }
+ for (var i=0; i < expressions.length; i++) {
+ var expression = expressions[i];
+ var element = JsUnitTest.$(elements[i]);
+ if (JsUnitTest.selectorMatch(expression, element)) {
+ pass = true;
+ break;
+ }
+ message = this.buildMessage('assertElementsMatch', 'In index <?>: expected <?> but got ?', index, expression, element);
+ this.flunk(message);
+ pass = false;
+ };
+ this.assert(pass, "Expected all elements to match.");
+ },
+
+ assertElementMatches: function(element, expression, message) {
+ this.assertElementsMatch([element], expression);
+ }
+};
+JsUnitTest.Unit.Runner = function(testcases) {
+ var argumentOptions = arguments[1] || {};
+ var options = this.options = {};
+ options.testLog = ('testLog' in argumentOptions) ? argumentOptions.testLog : 'testlog';
+ options.resultsURL = this.queryParams.resultsURL;
+ options.testLog = JsUnitTest.$(options.testLog);
+
+ this.tests = this.getTests(testcases);
+ this.currentTest = 0;
+ this.logger = new JsUnitTest.Unit.Logger(options.testLog);
+
+ var self = this;
+ JsUnitTest.Event.addEvent(window, "load", function() {
+ setTimeout(function() {
+ self.runTests();
+ }, 0.1);
+ });
+};
+
+JsUnitTest.Unit.Runner.prototype.queryParams = JsUnitTest.toQueryParams();
+
+JsUnitTest.Unit.Runner.prototype.portNumber = function() {
+ if (window.location.search.length > 0) {
+ var matches = window.location.search.match(/\:(\d{3,5})\//);
+ if (matches) {
+ return parseInt(matches[1]);
+ }
+ }
+ return null;
+};
+
+JsUnitTest.Unit.Runner.prototype.getTests = function(testcases) {
+ var tests = [], options = this.options;
+ if (this.queryParams.tests) tests = this.queryParams.tests.split(',');
+ else if (options.tests) tests = options.tests;
+ else if (options.test) tests = [option.test];
+ else {
+ for (testname in testcases) {
+ if (testname.match(/^test/)) tests.push(testname);
+ }
+ }
+ var results = [];
+ for (var i=0; i < tests.length; i++) {
+ var test = tests[i];
+ if (testcases[test])
+ results.push(
+ new JsUnitTest.Unit.Testcase(test, testcases[test], testcases.setup, testcases.teardown)
+ );
+ };
+ return results;
+};
+
+JsUnitTest.Unit.Runner.prototype.getResult = function() {
+ var results = {
+ tests: this.tests.length,
+ assertions: 0,
+ failures: 0,
+ errors: 0,
+ warnings: 0
+ };
+
+ for (var i=0; i < this.tests.length; i++) {
+ var test = this.tests[i];
+ results.assertions += test.assertions;
+ results.failures += test.failures;
+ results.errors += test.errors;
+ results.warnings += test.warnings;
+ };
+ return results;
+};
+
+JsUnitTest.Unit.Runner.prototype.postResults = function() {
+ if (this.options.resultsURL) {
+ // new Ajax.Request(this.options.resultsURL,
+ // { method: 'get', parameters: this.getResult(), asynchronous: false });
+ var results = this.getResult();
+ var url = this.options.resultsURL + "?";
+ url += "tests="+ this.tests.length + "&";
+ url += "assertions="+ results.assertions + "&";
+ url += "warnings=" + results.warnings + "&";
+ url += "failures=" + results.failures + "&";
+ url += "errors=" + results.errors;
+ JsUnitTest.ajax({
+ url: url,
+ type: 'GET'
+ })
+ }
+};
+
+JsUnitTest.Unit.Runner.prototype.runTests = function() {
+ var test = this.tests[this.currentTest], actions;
+
+ if (!test) return this.finish();
+ if (!test.isWaiting) this.logger.start(test.name);
+ test.run();
+ var self = this;
+ if(test.isWaiting) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ // setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+ setTimeout(function() {
+ self.runTests();
+ }, test.timeToWait || 1000);
+ return;
+ }
+
+ this.logger.finish(test.status(), test.summary());
+ if (actions = test.actions) this.logger.appendActionButtons(actions);
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+};
+
+JsUnitTest.Unit.Runner.prototype.finish = function() {
+ this.postResults();
+ this.logger.summary(this.summary());
+};
+
+JsUnitTest.Unit.Runner.prototype.summary = function() {
+ return new JsUnitTest.Template('#{tests} tests, #{assertions} assertions, #{failures} failures, #{errors} errors, #{warnings} warnings').evaluate(this.getResult());
+};
+JsUnitTest.Unit.Testcase = function(name, test, setup, teardown) {
+ this.name = name;
+ this.test = test || function() {};
+ this.setup = setup || function() {};
+ this.teardown = teardown || function() {};
+ this.messages = [];
+ this.actions = {};
+};
+// import JsUnitTest.Unit.Assertions
+
+for (method in JsUnitTest.Unit.Assertions) {
+ JsUnitTest.Unit.Testcase.prototype[method] = JsUnitTest.Unit.Assertions[method];
+}
+
+JsUnitTest.Unit.Testcase.prototype.isWaiting = false;
+JsUnitTest.Unit.Testcase.prototype.timeToWait = 1000;
+JsUnitTest.Unit.Testcase.prototype.assertions = 0;
+JsUnitTest.Unit.Testcase.prototype.failures = 0;
+JsUnitTest.Unit.Testcase.prototype.errors = 0;
+JsUnitTest.Unit.Testcase.prototype.warnings = 0;
+JsUnitTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port;
+
+// JsUnitTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port == 4711;
+
+JsUnitTest.Unit.Testcase.prototype.wait = function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+};
+
+JsUnitTest.Unit.Testcase.prototype.run = function(rethrow) {
+ try {
+ try {
+ if (!this.isWaiting) this.setup();
+ this.isWaiting = false;
+ this.test();
+ } finally {
+ if(!this.isWaiting) {
+ this.teardown();
+ }
+ }
+ }
+ catch(e) {
+ if (rethrow) throw e;
+ this.error(e, this);
+ }
+};
+
+JsUnitTest.Unit.Testcase.prototype.summary = function() {
+ var msg = '#{assertions} assertions, #{failures} failures, #{errors} errors, #{warnings} warnings\n';
+ return new JsUnitTest.Template(msg).evaluate(this) +
+ this.messages.join("\n");
+};
+
+JsUnitTest.Unit.Testcase.prototype.pass = function() {
+ this.assertions++;
+};
+
+JsUnitTest.Unit.Testcase.prototype.fail = function(message) {
+ this.failures++;
+ var line = "";
+ try {
+ throw new Error("stack");
+ } catch(e){
+ line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1];
+ }
+ this.messages.push("Failure: " + message + (line ? " Line #" + line : ""));
+};
+
+JsUnitTest.Unit.Testcase.prototype.warning = function(message) {
+ this.warnings++;
+ var line = "";
+ try {
+ throw new Error("stack");
+ } catch(e){
+ line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1];
+ }
+ this.messages.push("Warning: " + message + (line ? " Line #" + line : ""));
+};
+JsUnitTest.Unit.Testcase.prototype.warn = JsUnitTest.Unit.Testcase.prototype.warning;
+
+JsUnitTest.Unit.Testcase.prototype.info = function(message) {
+ this.messages.push("Info: " + message);
+};
+
+JsUnitTest.Unit.Testcase.prototype.error = function(error, test) {
+ this.errors++;
+ this.actions['retry with throw'] = function() { test.run(true) };
+ this.messages.push(error.name + ": "+ error.message + "(" + JsUnitTest.inspect(error) + ")");
+};
+
+JsUnitTest.Unit.Testcase.prototype.status = function() {
+ if (this.failures > 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ if (this.warnings > 0) return 'warning';
+ return 'passed';
+};
+
+JsUnitTest.Unit.Testcase.prototype.benchmark = function(operation, iterations) {
+ var startAt = new Date();
+ (iterations || 1).times(operation);
+ var timeTaken = ((new Date())-startAt);
+ this.info((arguments[2] || 'Operation') + ' finished ' +
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+ return timeTaken;
+};
+
+Test = JsUnitTest
54 lib/unittest.css
@@ -0,0 +1,54 @@
+body, div, p, h1, h2, h3, ul, ol, span, a, table, td, form, img, li {
+ font-family: sans-serif;
+}
+
+body {
+ font-size:0.8em;
+}
+
+#log {
+ padding-bottom: 1em;
+ border-bottom: 2px solid #000;
+ margin-bottom: 2em;
+}
+
+.logsummary {
+ margin-top: 1em;
+ margin-bottom: 1em;
+ padding: 1ex;
+ border: 1px solid #000;
+ font-weight: bold;
+}
+
+.logtable {
+ width:100%;
+ border-collapse: collapse;
+ border: 1px dotted #666;
+}
+
+.logtable td, .logtable th {
+ text-align: left;
+ padding: 3px 8px;
+ border: 1px dotted #666;
+}
+
+.logtable .passed {
+ background-color: #cfc;
+}
+
+.logtable .failed, .logtable .error {
+ background-color: #fcc;
+}
+
+.logtable .warning {
+ background-color: #FC6;
+}
+
+.logtable td div.action_buttons {
+ display: inline;
+}
+
+.logtable td div.action_buttons input {
+ margin: 0 5px;
+ font-size: 10px;
+}

0 comments on commit 3eda838

Please sign in to comment.
Something went wrong with that request. Please try again.