/** * TestCase - JavaScript testing framework, version 2.0.2 * Copyright (C) 2007-2008 Nikolay V. Nemshilov aka St. * * The library is freely distributable under the terms of an MIT-style license * For details, see the TestCase web site: http://testcase.rubyforge.net */ var TestCaseUtil = { create_class: function() { var superklass = null, params = this.to_a(arguments); if (typeof params[0] == 'function') superklass = params.shift(); var klass = function() { return this.initialize ? this.initialize.apply(this, arguments) : this; } if (superklass) { var subclass = function() { }; subclass.prototype = superklass.prototype; klass.prototype = new subclass; klass.superclass = superklass; } klass.prototype.constructor = klass; if (params[0]) this.extend(klass.prototype, params[0]); return klass; }, extend: function(object, properties, dont_overwrite) { for (key in properties) if (!(dont_overwrite && typeof object[key] != 'undefined')) object[key] = properties[key]; return object; }, extend_with_camelized_aliases: function(object, mixin) { var camelized_mixin = {}; for (key in mixin) { var parts = key.split('_'), camelized = parts[0]; for (var i=1; i < parts.length; i++) camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); camelized_mixin[camelized] = mixin[key]; } return TestCaseUtil.extend(object, camelized_mixin); }, extend_fully_object_and_prototype: function(object, mixin) { TestCaseUtil.extend(object, mixin); TestCaseUtil.extend(object.prototype, mixin); TestCaseUtil.extend_with_camelized_aliases(object, mixin); return TestCaseUtil.extend_with_camelized_aliases(object.prototype, mixin); }, equal: function(obj1, obj2) { return TestCaseUtil.to_s(obj1) == TestCaseUtil.to_s(obj2); }, to_a: function(it) { var a = []; for (var i=0; i < it.length; i++) a.push(it[i]); return a; }, to_s: function(object, one_level) { var value = this._try_to_s(object); if (value !== object) return value; var entries = this._get_object_stringified_entries( one_level ? this._one_level_to_s : this.to_s, object ); return object instanceof Array ? '['+ entries.join(', ')+ ']' : '{' + entries.join(', ') + '}'; }, debug: function(object) { return this.to_s(object, true); }, MAX_OBJECT_STRING_LENGTH: 80, to_pretty_s: function(object, offset) { if (object == null) { return 'null'; } else if (typeof object != 'object') { return this.to_s(object); } else { var offset = offset || 0; var object_s = this.to_s(object); if (object_s.length > this.MAX_OBJECT_STRING_LENGTH) { if (object instanceof Array) { var len = object.length; } else { var len = 0; for (var key in object) { len++; } } if (len > 1) { var entries = this._get_object_stringified_entries( this.to_pretty_s, object, offset + 1 ); var offset_s = ''; for (var i=0; i < offset; i++) { offset_s += ' '; } object_s = object instanceof Array ? "[" + entries.join(",\n "+ offset_s) + "\n"+offset_s+"]" : "{" + entries.join(",\n "+ offset_s) + "\n"+offset_s+"}" ; } } return object_s; } }, _get_object_stringified_entries: function(method, object, param) { var entries = []; if (object instanceof Array) { for (var i=0; i < object.length; i++) { entries.push(method.apply(this, [object[i], param])); } } else { for (var property in object) { entries.push(this.to_s(property) + ': ' + method.apply(this, [object[property], param])); } } return entries; }, _one_level_to_s: function(object) { var value = this._try_to_s(object); if (value !== object) { return value; } else if (object instanceof Array) { return '[...]'; } else { return '{...}'; } }, _try_to_s: function(object) { switch (typeof object) { case 'undefined': case 'unknown': return 'undefined'; case 'string': return '"'+object+'"'; case 'number': return ''+object; case 'function': case 'boolean': return object.toString(); } if (object === null) return 'null'; if (object.nodeType == 1) return '<'+object.tagName+ (object.id ? ' id="'+object.id+'"':'')+ (object.className ? ' class="'+object.className+'"':'')+ (object.title ? ' title="'+object.title+'"':'')+ (object.type ? ' type="'+object.type+'"':'')+ '>'+object.innerHTML+''; return object; }, get_object_class_name: function(object) { if (object === null) { return 'Null'; } switch (typeof object) { case 'string': return 'String'; case 'boolean': return 'Boolean'; case 'number': return 'Number'; case 'function': return 'Function'; case 'object': if (typeof object.constructor != 'undefined') { var match = object.constructor.toString().match(/function\s*(\w+)/); if (match && match.length == 2) { return match[1]; } for (var key in self) { try { if (object instanceof self[key]) return key; } catch (e) {} } if (object instanceof Array) { return 'Array'; } else { return 'Object'; } } } return 'Unknown'; }, get_class_name: function(klass) { if (klass === null) { return 'Null'; } switch (klass) { case String: return 'String'; case Boolean: return 'Boolean'; case Number: return 'Number'; case Function: return 'Function'; case Array: return 'Array'; case Object: return 'Object'; default: for (var key in self) { if (klass == self[key]) return key; } } return 'Unknown'; }, Browser: { IE: !!(window.attachEvent && !window.opera), Opera: !!window.opera, WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/), Konqueror: navigator.userAgent.indexOf('Konqueror') != -1 }, $: function(element) { return typeof element == 'string' ? document.getElementById(element) : element; }, $$: function(css_rule) { return this.Element.select(document, css_rule); } }; /* ------------------------------------------------------------------- */ TestCaseUtil.Array = { include: function(list, item) { for (var i=0; i < list.length; i++) if (list[i] == item) return true; return false; }, merge: function(list1, list2) { for (var i=0; i < list2.length; i++) { if (!this.include(list1, list2[i])) list1.push(list2[i]); } return list1; }, without: function(list, item) { var new_list = []; for (var i=0; i < list.length; i++) if (list[i] != item) new_list.push(list[i]); return new_list; }, uniq: function(list) { var new_list = []; for (var i=0; i < list.length; i++) if (!this.include(new_list, list[i])) new_list.push(list[i]); return new_list; }, find: function(list, callback) { for (var i=0; i < list.length; i++) if (callback(list[i])) return list[i]; return null; }, each: function(list, callback) { for (var i=0; i < list.length; i++) callback(list[i]); return list; } }; /* ------------------------------------------------------------------- */ TestCaseUtil.Event = { KEY_UP: 38, observe: function(element, name, callback) { var element = TestCaseUtil.$(element); if (element.addEventListener) { element.addEventListener(name, callback, false); } else { element.attachEvent("on" + name, callback); } return element; }, stop: function(event) { if (event.stopPropagation) { event.stopPropagation(); event.preventDefault(); } else { event.cancelBubble = true; event.returnValue = false; } }, element: function(event) { var target = event.srcElement ? event.srcElement : event.target; return (!target || target.nodeType == 1) ? target : target.parentNode; }, pointerX: function(event) { return this.pointer(event).x; }, pointerY: function(event) { return this.pointer(event).y; }, pointer: function(event) { return { x: event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)), y: event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop)) }; } }; /* ------------------------------------------------------------------- */ TestCaseUtil.Element = { visible: function(element) { return TestCaseUtil.$(element).style.display != 'none'; }, show: function(element) { TestCaseUtil.$(element).style.display = ''; }, hide: function(element) { TestCaseUtil.$(element).style.display = 'none'; }, toggle: function(element) { if (this.visible(element)) this.hide(element); else this.show(element); }, up: function(element, css_rule) { return this.Selector.match_atom(element, css_rule) ? element : (element.parentNode ? this.up(element.parentNode, css_rule) : null); }, down: function(element, css_rule) { return this.Selector.find_first(element, css_rule); }, select: function(element, css_rule) { return this.Selector.find_all(element, css_rule); }, has_class_names: function(element, class_names) { for (var i=0; i < class_names.length; i++) if (!this.has_class_name(element, class_names[i])) return false; return true; }, has_class_name: function(element, class_name) { var elementClassName = element.className; return (elementClassName && elementClassName.length > 0 && (elementClassName == class_name || new RegExp("(^|\\s)" + class_name + "(\\s|$)").test(elementClassName))); }, add_class_name: function(element, class_name) { if (!this.has_class_name(element, class_name)) element.className += (element.className ? ' ' : '') + class_name; return element; }, remove_class_name: function(element, class_name) { element.className = element.className.replace( new RegExp("(^|\\s+)" + class_name + "(\\s+|$)"), ' ').replace(/^\s+|\s+$/g, ''); return element; }, has_attributes: function(element, attrs) { for (key in attrs) if (attrs[key] != null && element[key] != attrs[key]) return false; return true; } }; /* ------------------------------------------------------------------- */ TestCaseUtil.Element.Selector = { find_first: function(element, css_rule) { var founds = this.find_all(element, css_rule); return founds.length ? founds[0] : null; }, find_all: function(element, css_rule) { var founds = [], element = TestCaseUtil.$(element); if (typeof css_rule == 'string') css_rule = this._parse_rule(css_rule); if (css_rule[0] instanceof Array) { for (var i=0; i < css_rule.length; i++) TestCaseUtil.Array.merge(founds, this.find_all(element, css_rule[i])); } else { var css_atom = css_rule.shift(); switch (css_atom.rel) { case '>': founds = this._find_immidiate_descendants(element, css_atom); break; case '+': founds = this._find_next_sibling(element, css_atom); break; case '~': founds = this._find_late_siblings(element, css_atom); break; default: founds = this._find_descendants(element, css_atom); } if (css_rule.length > 0) { var sub_founds = []; for (var i=0; i < founds.length; i++) { var css_rule_clone = []; for (var j=0; j < css_rule.length; j++) css_rule_clone.push(TestCaseUtil.extend({}, css_rule[j])); TestCaseUtil.Array.merge(sub_founds, this.find_all(founds[i], css_rule_clone)); } founds = sub_founds; } } return founds; }, _find_descendants: function(element, css_atom, immidiates_only) { var founds = [], child = element.firstChild; while (child) { if (child.nodeType == 1) { if (this._fully_match_atom(child, css_atom, element) && !TestCaseUtil.Array.include(founds, child)) { founds.push(child); } if (!immidiates_only) TestCaseUtil.Array.merge(founds, this._find_descendants(child, css_atom)); } child = child.nextSibling; } return founds; }, _find_immidiate_descendants: function(element, css_atom) { return this._find_descendants(element, css_atom, true); }, _find_next_sibling: function(element, css_atom) { var founds = [], node = element.nextSibling; while (node) { if (node.nodeType == 1) { if (this._fully_match_atom(node, css_atom, element) && !TestCaseUtil.Array.include(founds, node)) founds.push(node); break; } node = node.nextSibling; } return founds; }, _find_late_siblings: function(element, css_atom) { var founds = [], node = element.nextSibling; while (node) { if (node.nodeType == 1) { if (this._fully_match_atom(node, css_atom, element) && !TestCaseUtil.Array.include(founds, node)) founds.push(node); } node = node.nextSibling; } return founds; }, _fully_match_atom: function(element, css_atom, parent_node) { return this.match_atom(element, css_atom) && this._match_pseudo(parent_node, element, css_atom.pseudo); }, _match_pseudo: function(element, child, pseudo) { switch (pseudo) { case 'first-child': return this._is_first_child(element, child); case 'last-child': return this._is_last_child(element, child); case 'only-child': return this._is_only_child(element, child); default: return true; } }, _is_first_child: function(element, child) { var node = element.firstChild; while (node) { if (node.nodeType == 1) return node == child; node = node.nextSibling; } return false; }, _is_last_child: function(element, child) { var node = element.lastChild; while (node) { if (node.nodeType == 1) return node == child; node = node.previousSibling; } return false; }, _is_only_child: function(element, child) { var found = null, node = element.firstChild; while (node) { if (node.nodeType == 1) { if (found) return false; else found = node; } node = node.nextSibling; } return found == child; }, match_atom: function(element, css_atom) { if (typeof css_atom == 'string') css_atom = TestCaseUtil.Element.Selector._parse_atom(css_atom); return (css_atom.tag_name == '*' || element.tagName == css_atom.tag_name) && TestCaseUtil.Element.has_class_names(element, css_atom.class_names) && TestCaseUtil.Element.Selector._match_css_attributes(element, css_atom.attrs) && (!css_atom.id || element.id == css_atom.id); }, _match_css_attributes: function(element, attrs_def) { for (var i=0; i < attrs_def.length; i++) { var match = false; switch (attrs_def[i].type) { case '|=': match = (typeof element[attrs_def[i].name] == 'string') && ( element[attrs_def[i].name] == attrs_def[i].value || new RegExp("(^|\-)" + attrs_def[i].value + "(\-|$)").test(element[attrs_def[i].name]) ); break; case '~=': match = (typeof element[attrs_def[i].name] == 'string') && ( element[attrs_def[i].name] == attrs_def[i].value || new RegExp("(^|\\s)" + attrs_def[i].value + "(\\s|$)").test(element[attrs_def[i].name]) ); break; default: match = element[attrs_def[i].name] == attrs_def[i].value; } if (!match) return false; } return true; }, _parse_rule: function(css_rule) { var rules = css_rule.split(','); var recognized_rules = []; for (var i=0; i < rules.length; i++) { var rule = rules[i].replace(/^\s+|\s+$/g, ''); var match, separator_pos, recognized_rule = []; var relation = null; while ((match = rule.match(/(\s*([~>+ ])\s*)[^=]/))) { separator_pos = rule.indexOf(match[0]); recognized_rule.push({ rule: rule.substring(0, separator_pos).replace(/^\s+|\s+$/g, ''), rel: relation }); relation = match[2]; rule = rule.substr(separator_pos+(match[1].length==1 ? 1 : match[1].length-1)).replace(/^\s+|\s+$/g, ''); } recognized_rule.push({ rule: rule, rel: relation }); for (var j=0; j < recognized_rule.length; j++) TestCaseUtil.extend(recognized_rule[j], this._parse_atom(recognized_rule[j].rule)); recognized_rules.push(recognized_rule); } return recognized_rules; }, _id_re: /#[^\[\.]*/, _attrs_re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/, _parse_atom: function(rule) { var atom = { tag_name: '*', id: null, class_names: [], pseudo: null, attrs: [] }; var els = rule.split(':'); if (els.length > 1) { atom.pseudo = els[1].toLowerCase(); rule = els[0]; } while ((attr_match = rule.match(this._attrs_re))) { atom.attrs.push({ name: attr_match[1], type: attr_match[2], value: attr_match[5] }); rule = rule.replace(attr_match[0], ''); } if ((id_match = rule.match(this._id_re))) { atom.id = id_match[0].substr(1); rule = rule.replace(id_match[0], ''); } atom.class_names = rule.split('.'); atom.tag_name = atom.class_names.shift().toUpperCase(); if (atom.tag_name == '') atom.tag_name = '*'; return atom; } }; /* ------------------------------------------------------------------- */ TestCaseUtil.Diff = { calc: function(obj1, obj2) { if (TestCaseUtil.get_object_class_name(obj1) != TestCaseUtil.get_object_class_name(obj2)) throw "The given objects are instances of different classes"; if (obj1 == null || obj2 == null) throw "One of the objects is null"; if (obj1 == obj2) return []; switch (typeof obj1) { case 'string': return TestCaseUtil.Diff._string_diff.apply(TestCaseUtil.Diff, [obj1, obj2]); case 'function': return TestCaseUtil.Diff._string_diff.apply(TestCaseUtil.Diff, [obj1.toString(), obj2.toString()]); case 'boolean': case 'number': return obj1 == obj2 ? [] : [{del: obj1}, {ins: obj2}]; default: return TestCaseUtil.Diff[obj1 instanceof Array ? '_array_diff' : '_object_diff'].apply(TestCaseUtil.Diff, [obj1, obj2]); } }, _string_diff: function(str1, str2) { return this.__compact( this.__calc_diff(str1.split("\n"), str2.split("\n")), function (group, entry) { return group +"\n"+ entry; } ); }, _array_diff: function(list1, list2) { var diff = this.__calc_diff(list1, list2); for (var i=0; i < diff.length; i++) { var key = 'eql'; if (typeof diff[i]['del'] != 'undefined') { key = 'del'; } else if (typeof diff[i]['ins'] != 'undefined') { key = 'ins'; } } return diff; }, _object_diff: function(obj1, obj2) { var lists = [[], []], objects = [obj1, obj2]; for (var i=0; i < 2; i++) { for (var key in objects[i]) { lists[i].push( key+"{:_key-sep_:}"+TestCaseUtil.to_s(objects[i][key]) ); } } var diff = this.__calc_diff(lists[0], lists[1]); var collect = { eql: {}, ins: {}, del: {} }; for (var i=0; i < diff.length; i++) { var key = 'eql'; if (diff[i]['ins']) { key = 'ins'; } else if (diff[i]['del']) { key = 'del'; } var a_key = diff[i][key].split("{:_key-sep_:}")[0]; collect[key][a_key] = (diff[i]['ins'] ? obj2 : obj1)[a_key]; } var diff = [], keys = ['del', 'ins', 'eql']; for (var i=0; i < keys.length; i++) { if (!TestCaseUtil.equal(collect[keys[i]], {})) { var el = {}; el[keys[i]] = collect[keys[i]]; diff.push(el); } } return diff; }, __calc_diff: function(first, second) { var diff = [], start_k = 0; for (var i=0; i < first.length; i++) { var eql_found = false; var inses = []; for (var k=start_k; k < second.length; k++) { if (TestCaseUtil.equal(first[i], second[k])) { eql_found = second[k]; start_k = k+1; break; } else { inses.push(second[k]); } } if (eql_found) { for (var j=0; j < inses.length; j++) { diff.push({ins: inses[j]}); } diff.push({eql: eql_found}); } else { diff.push({del: first[i]}); } } for (var k=start_k; k < second.length; k++) { diff.push({ins: second[k]}); } var only_eqls = true; for (var i=0; i < diff.length; i++) { if (diff[i]['del'] || diff[i]['ins']) { only_eqls = false; break; } } return only_eqls ? [] : diff; }, __compact: function(diff, compactor) { if (diff.length == 0) { return diff; } var c_diff = []; var last_o = diff[0]; for (var i=1; i < diff.length; i++) { var key = 'eql'; if (typeof diff[i]['ins'] != 'undefined') { key = 'ins'; } else if (typeof diff[i]['del'] != 'undefined') { key = 'del'; } if (typeof last_o[key] != 'undefined') { last_o[key] = compactor(last_o[key], diff[i][key]); } else { c_diff.push(last_o); last_o = diff[i]; } } c_diff.push(last_o); return c_diff; } }; /* ------------------------------------------------------------------- */ TestCaseUtil.Cookie = { enabled: function() { document.cookie = "test_cookie=test"; return document.cookie.indexOf("test_cookie=test")!=-1; }, set: function(key, value, keep_days) { if (keep_days) { var date = new Date();date.setTime(date.getTime()+(100*24*60*60*1000)); } document.cookie = key+"="+escape(value)+(keep_days ? "; expires="+date.toGMTString() : ""); }, get: function(key) { var value = '', pairs = document.cookie.split(";"); for (var i=0; i < pairs.length; i++) { var pair = pairs[i].split('='); if (pair.length == 2 && pair[0].replace(/^\s+|\s+$/g, '') == key) value = pair[1].replace(/^\s+|\s+$/g, ''); } return unescape(value); } }; /* ------------------------------------------------------------------- */ var TestCase = TestCaseUtil.create_class(); TestCaseUtil.extend(TestCase, { version: "2.0.2", create: function(defs) { var Test = TestCaseUtil.create_class(); TestCaseUtil.extend(Test, TestCase); Test.prototype = new TestCase(); Test.extend = function(entries) { TestCaseUtil.extend(this.prototype, entries); this.wire_custom_assertions(entries); }; Test.extend(defs || {}); return Test; } }); TestCaseUtil.extend(TestCase.prototype, { name: null, title: null, size: 0, failed: 0, passed: 0, assertions: 0, initialize: function(title) { this.title = (this.name || "Unnamed Test") + (title ? ": "+title : ""); this.size = 0; this.passed = 0; this.assertations = 0; for (key in this) { if (key.substring(0, 4) == "test") { this.size ++; } } } }); /* ------------------------------------------------------------------- */ TestCase.Starter = { run: function(title) { var test = this._get_test_instance(); this._register_test(test); test.passed = 0; test.failed = 0; test.assertions = 0; if (this.reporter && this.reporter.ui_builder && this.reporter.ui_builder.test_is_skipped(test)) { test.skipped = true; this.reporter.ui_builder.create_summary_report(this.reporter.current_test_block, test); return test; } this._call_before_all(test); try { var test_names = this._get_test_methods(test); for (var i=0; i < test_names.length; i++) { var name = test_names[i]; this._report_test_started(name); this._call_setup(test); test.manual_failure = null; var test_start_assertion_num = test.assertions; try { test[name](); if (test.manual_failure) throw new TestCase.Exception(test.manual_failure); test.passed++; this._report_test_passed(name); } catch (e) { if (e instanceof TestCase.Exception) { test.failed++; this._report_failed(name, e, test.assertions - test_start_assertion_num + 1); } else { throw e; } } finally { this._call_teardown(test); } } } finally { this._call_after_all(test); } return test; }, run_on_load: function() { var $this = this; TestCaseUtil.Event.observe(window, 'load', function() { $this.run(); }); }, _get_test_instance: function(title) { if (typeof this == 'object') { var test = this; } else { var Test = this; var test = new Test(title); } return test; }, _get_test_methods: function(test) { var test_method_names = []; for (key in test) if (key.substring(0, 4) == "test" && typeof test[key] == 'function') test_method_names.push(key); if (navigator.userAgent.indexOf("MSIE") != -1) { test_method_names.reverse(true); } return test_method_names; }, _call_setup: function(test) { if (test['setup']) test.setup.apply(test); else if (test['setUp']) test.setUp.apply(test); }, _call_teardown: function(test) { if (test['teardown']) test.teardown.apply(test); else if (test['tearDown']) test.tearDown.apply(test); }, _call_before_all: function(test) { if (test['before_all']) test.before_all.apply(test); else if (test['beforeAll']) test.beforeAll.apply(test); }, _call_after_all: function(test) { if (test['after_all']) test.after_all.apply(test); else if (test['afterAll']) test.afterAll.apply(test); }, _register_test: function(test) { if (this.reporter) { this.reporter.register(test); } }, _report_test_started: function(test_name) { if (this.reporter) { this.reporter.test_started(test_name); } }, _report_test_passed: function(test_name) { if (this.reporter) { this.reporter.test_passed(test_name); } }, _report_failed: function(test_name, error, assertion_num) { if (this.reporter) { this.reporter.test_failed(test_name, error, assertion_num); } else { throw error; } } }; /* ------------------------------------------------------------------- */ TestCase.Mocking = { mock: function(object, name, mock) { if (!object[name]) throw "The object have no method '"+name+"' to mock it up"; var o_mocks = TestCase.Mocking._get_mocks_of(object); o_mocks[name] = o_mocks[name] || object[name]; return object[name] = mock; }, undo_mock: function(object, name) { if (this._has_mock_for(object, name)) { var o_mocks = TestCase.Mocking._get_mocks_of(object); if (o_mocks[name]) { object[name] = o_mocks[name]; o_mocks[name] = null; } } return object[name]; }, with_mock: function(object, name, mock, func, scope) { this.mock(object, name, mock); try { func.apply(scope); } finally { this.undo_mock(object, name); } }, mock_effects: function() { this._run_if_mockable('mock_effects'); }, undo_effects_mock: function() { this._run_if_mockable('undo_effects_mock'); }, with_effects_mock: function(func, scope) { this.mock_effects(); try { func.apply(scope); } finally { this.undo_effects_mock(); } }, mock_ajax: function(options) { this._run_if_mockable('mock_ajax', options); }, undo_ajax_mock: function() { this._run_if_mockable('undo_ajax_mock'); }, with_ajax_mock: function(options, func, scope) { this.mock_ajax(); try { func.apply(scope); } finally { this.undo_ajax_mock(); } }, __mock_ups: [], _get_mocks_of: function(object) { var o_mocks = null; var mockups = TestCase.Mocking.__mock_ups; for (var i=0; i < mockups.length; i++) { if (mockups[i]['object'] == object) { o_mocks = mockups[i]; break; } } if (!o_mocks) { o_mocks = { 'object': object, 'mocks': {} }; TestCase.Mocking.__mock_ups.push(o_mocks); } return o_mocks['mocks']; }, _has_mock_for: function(object, name) { var mockups = TestCase.Mocking.__mock_ups; for (var i=0; i < mockups.length; i++) { if (mockups[i]['object'] == object) { return typeof mockups[i]['mocks'][name] != 'undefined' && mockups[i]['mocks'][name] != null; } } return false; }, _mockable_libs: ['Prototype', 'MooTools', 'jQuery'], _run_if_mockable: function() { var args = TestCaseUtil.to_a(arguments); var method_name = args.shift(); if (method_name == 'mock_ajax') { this._raw_mockup_ajax.apply(this, args); } else if (method_name == 'undo_ajax_mock') { this._raw_undo_ajax_mockup(); } for (var i=0; i < this._mockable_libs.length; i++) { if (self[this._mockable_libs[i]]) { return this[this._mockable_libs[i]][method_name].apply(this[this._mockable_libs[i]], args); } } }, _raw_mockup_ajax: function(params) { if (TestCaseUtil.Browser.IE) { this.__ms_raw_ajax_mock(params); } else { this.__w3c_raw_ajax_mock(params); } }, __w3c_raw_ajax_mock: function(params) { if (!(new XMLHttpRequest instanceof TestCase.Mocking.FakeXMLHttpRequest)) { this.mock(self, 'XMLHttpRequest', function() { return new TestCase.Mocking.FakeXMLHttpRequest(params); }); } }, __ms_raw_ajax_mock: function(params) { if (!(new ActiveXObject('Msxml2.XMLHTTP') instanceof TestCase.Mocking.FakeXMLHttpRequest)) { var _ = ActiveXObject; this.mock(self, 'ActiveXObject', function(param) { if (param.indexOf('XMLHTTP') != -1) { return new TestCase.Mocking.FakeXMLHttpRequest(params); } else { return new _(param); } }); } }, _raw_undo_ajax_mockup: function() { if (TestCaseUtil.Browser.IE) { this.undo_mock(self, 'ActiveXObject'); } else { this.undo_mock(self, 'XMLHttpRequest'); } } }; /* ------------------------------------------------------------------- */ TestCase.Mocking.FakeXMLHttpRequest = TestCaseUtil.create_class({ onreadystatechange: null, readyState: null, status: null, statusText: null, responseText: null, responseXML: null, fakeData: null, initialize: function(options) { this.fakeData = TestCaseUtil.extend({ status: 200, xml: null, text: '', headers: {} }, options || {}); this.onreadystatechange = function() {}; this.readyState = 0; }, __onSend: function() { this.readyState = 2; this.status = this.fakeData.status; this.onreadystatechange(); this.readyState = 3; this.responseText = ''; this.onreadystatechange(); this.readyState = 4; this.responseText = this.fakeData.text; this.responseXML = this.fakeData.xml; this.onreadystatechange(); }, open: function(method, url, async, user, password) { this.readyState = 1; }, send: function(body) { this.__onSend(); }, setRequestHeader: function(name, value) { }, getResponseHeader: function(name) { for (var key in this.fakeData.headers) { if (key.toLowerCase() == name.toLowerCase()) return this.fakeData.headers[key]; } return null; }, getAllResponseHeaders: function() { var headers = []; for (var key in this.fakeData.headers) { headers.push(key+": "+this.fakeData.headers[key]); } return headers.join("\n\n"); }, overrideMimeType: function(mimetype) {}, addEventListener: function(type, listener, useCapture) {}, removeEventListener: function(type, listener, useCapture) {}, dispatchEvent: function(event) {}, dispatchException: function(e) { throw e; }, abort: function() {} }); /* ------------------------------------------------------------------- */ TestCase.Mocking.Prototype = { hide_effects: ['Fade', 'SlideUp', 'BlindUp', 'SwitchOff'], show_effects: ['Appear', 'SlideDown', 'BlindDown', 'Grow'], mock_effects: function() { if (!self['Effect']) return false; for (var i=0; i < this.hide_effects.length; i++) { TestCase.mock(Effect, this.hide_effects[i], function(element, options) { Element.hide(element); if (options.afterFinish) options.afterFinish(); return element; }); TestCase.mock(Effect, this.show_effects[i], function(element, options) { Element.show(element); if (options.afterFinish) options.afterFinish(); return element; }); } }, undo_effects_mock: function() { if (!self['Effect']) return false; for (var i=0; i < this.hide_effects.length; i++) { TestCase.undo_mock(Effect, this.hide_effects[i]); TestCase.undo_mock(Effect, this.show_effects[i]); } }, mock_ajax: function(options) { if (!self['Ajax']) { return false; } TestCase.mock(Ajax, 'getTransport', function() { return new TestCase.Mocking.FakeXMLHttpRequest(options); }); }, undo_ajax_mock: function() { if (!self['Ajax']) { return false; } TestCase.undo_mock(Ajax, 'getTransport'); } }; /* ------------------------------------------------------------------- */ TestCase.Mocking.MooTools = { mock_effects: function() { if (!self['Fx']) { return false; } if (Fx['Tween']) { TestCase.mock(Fx.Tween.prototype, 'start', function(property, from, to) { this.onStart(); this.set(property, from); if (to) { this.set(property, to); } this.onComplete(); }); } if (Fx['Morph']) { TestCase.mock(Fx.Morph.prototype, 'start', function(properties) { this.onStart(); var from = {}, to = {}; for (var key in properties) { if (properties[key] instanceof Array) { from[key] = properties[key][0]; to[key] = properties[key][1]; } else { from[key] = properties[key]; } } this.set(from); if (!TestCaseUtil.equal(to, {})) { this.set(to); } this.onComplete(); }); } }, undo_effects_mock: function() { if (!self['Fx']) { return false; } if (Fx['Tween']) TestCase.undo_mock(Fx.Tween.prototype, 'start'); if (Fx['Morph']) TestCase.undo_mock(Fx.Morph.prototype, 'start'); }, mock_ajax: function(options) { if (!self['Request']) { return false; } var old_initialize = Request.prototype.initialize; TestCase.mock(Request.prototype, 'initialize', function() { old_initialize.apply(this, arguments); this.xhr = new TestCase.Mocking.FakeXMLHttpRequest(options); }); }, undo_ajax_mock: function() { if (!self['Request']) { return false; } TestCase.undo_mock(Request.prototype, 'initialize'); } }; /* ------------------------------------------------------------------- */ TestCase.Mocking.jQuery = { mock_effects: function() { }, undo_effects_mock: function() { }, mock_ajax: function(options) { }, undo_ajax_mock: function() { } }; /* ------------------------------------------------------------------- */ TestCase.Fires = { FIRES_DEFAULT_MOUSE_EVENT_OPTIONS: { pointerX: 0, pointerY: 0, button: 0, bubbles: true, cancelable: true, altKey: false, ctrlKey: false, shiftKey: false, metaKey: false }, FIRES_DEFAULT_KEYBOARD_EVENT_OPTIONS: { keyCode: 0, charCode: 0, bubbles: true, cancelable: true, altKey: false, ctrlKey: false, shiftKey: false, metaKey: false }, fire_mouse_event: function(element, eventName, options) { return this._fire_event(this._$(element), this._create_mouse_event(this._$(element), eventName, options) ); }, fire_key_event: function(element, eventName, keyCode, options) { return this._fire_event(this._$(element), this._create_key_event(this._$(element), eventName, TestCaseUtil.extend((options || {}), {keyCode: keyCode, charCode: keyCode}) ) ); }, fire_click: function(element, options) { return this.fire_mouse_event(element, 'click', options); }, fire_middle_click: function(element, options) { return this.fire_mouse_event(element, 'middleclick', TestCaseUtil.extend({button: 1}, options || {})); }, fire_right_click: function(element, options) { return this.fire_mouse_event(element, 'rightclick', TestCaseUtil.extend({button: 2}, options || {})); }, fire_double_click: function(element, options) { return this.fire_mouse_event(element, 'dblclick', options); }, fire_mouse_down: function(element, options) { return this.fire_mouse_event(element, 'mousedown', options); }, fire_mouse_up: function(element, options) { return this.fire_mouse_event(element, 'mouseup', options); }, fire_mouse_over: function(element, options) { return this.fire_mouse_event(element, 'mouseover', options); }, fire_mouse_out: function(element, options) { return this.fire_mouse_event(element, 'mouseout', options); }, fire_mouse_move: function(element, options) { return this.fire_mouse_event(element, 'mousemove', options); }, fire_key_press: function(element, key, options) { return this.fire_key_event(element, 'keypress', key, options); }, fire_key_down: function(element, key, options) { return this.fire_key_event(element, 'keydown', key, options); }, fire_key_up: function(element, key, options) { return this.fire_key_event(element, 'keyup', key, options); }, _$: function(element) { element = TestCaseUtil.$(element); if (element == document && document.createEvent && !element.dispatchEvent) element = document.documentElement; return element; }, _fire_event: function(element, event) { if (document.createEvent) { element.dispatchEvent(event); } else { if (TestCaseUtil.Element.up(element, 'body')) { if (event.eventType != 'onmiddleclick') element.fireEvent(event.eventType, event); } else { this.fail("Please put your element on the page to fire an event on it in IE"); } } return event; }, _create_mouse_event: function(element, eventName, options) { return (document.createEvent ? this._create_w3c_mouse_event : this._create_ie_mouse_event).apply( this, [element, eventName, TestCaseUtil.extend(this.FIRES_DEFAULT_MOUSE_EVENT_OPTIONS, options || {})]); }, _create_w3c_mouse_event: function(element, eventName, options) { var event = document.createEvent("MouseEvents"); event.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView, eventName == 'dblclick' ? 2 : 1, options.pointerX, options.pointerY, options.pointerX, options.pointerY, options.ctrlKey, options.altKey, options.shiftKey, options.metakey, options.button, element ); event.eventName = eventName; return event; }, _create_ie_mouse_event: function(element, eventName, options) { var event = this._create_ie_event(eventName, options); event.clientX = options.pointerX; event.clientY = options.pointerY; event.button = options.button == 1 ? 4 : options.button; return event; }, _create_key_event: function(element, eventName, options) { return (document.createEvent ? this._create_w3c_keyboard_event : this._create_ie_keyboard_event).apply( this, [eventName, TestCaseUtil.extend(this.FIRES_DEFAULT_KEYBOARD_EVENT_OPTIONS, options || {})]); }, _create_w3c_keyboard_event: function(eventName, options) { return (TestCaseUtil.Browser.Gecko ? this._create_gecko_keyboard_event : ( TestCaseUtil.Browser.WebKit ? this._create_webkit_keyboard_event : this._create_dom2_ui_event )).apply(this, [eventName, options]); }, _create_gecko_keyboard_event: function(eventName, options) { var event = document.createEvent("KeyboardEvent"); event.initKeyEvent(eventName, options.bubbles, options.cancelable, document.defaultView, options.ctrlKey, options.altKey, options.shiftKey, options.metakey, options.keyCode, options.charCode ); event.eventName = eventName; return event; }, _create_webkit_keyboard_event: function(eventName, options) { var event = document.createEvent("KeyboardEvent"); event.initKeyboardEvent(eventName, options.bubbles, options.cancelable, document.defaultView, null, 0, options.ctrlKey, options.altKey, options.shiftKey, options.metakey ); event.eventName = eventName; return event; }, _create_dom2_ui_event: function(eventName, options) { var event = document.createEvent("UIEvent"); event.initUIEvent(eventName, options.bubbles, options.cancelable, document.defaultView, 1); event.keyCode = options.keyCode; event.charCode = options.charCode; event.altKey = options.altKey; event.metaKey = options.metaKey; event.ctrlKey = options.ctrlKey; event.shiftKey = options.shiftKey; event.eventName = eventName; return event; }, _create_ie_keyboard_event: function(eventName, options) { var event = this._create_ie_event(eventName, options); event.keyCode = options.keyCode; return event; }, _create_ie_event: function(eventName, options) { var event = document.createEventObject(); event.eventName = eventName == 'rightclick' ? 'contextmenu' : eventName; event.type = event.eventType = "on" + event.eventName; event.altKey = options.altKey; event.ctrlKey = options.ctrlKey; event.shiftKey = options.shiftKey; return event; } }; /* ------------------------------------------------------------------- */ TestCase.Exception = TestCaseUtil.create_class(Error, { test: null, assertion: null, message: null, initialize: function(test, assertion, message) { this.test = test; this.assertion = assertion; this.message = message; }, toString: function() { return "TestCase.Exception: "+this.message; } }); TestCase.ProblemException = TestCaseUtil.create_class(TestCase.Exception, { problem: null, initialize: function(test, assertion, problem, message) { this.test = test; this.assertion = assertion; this.problem = problem; this.message = message; }, toString: function() { return "TestCase.ProblemException: "+this.problem+(this.message ? " ("+this.message+")" : ""); } }); TestCase.WrongValueException = TestCaseUtil.create_class(TestCase.Exception, { expected: null, received: null, format: false, initialize: function(test, assertion, expected, received, message, format) { this.test = test; this.assertion = assertion; this.expected = expected; this.received = received; this.message = message; this.format = format; }, toString: function() { return "TestCase.WrongValueException: "+this.expected+" -> "+this.received; } }); /* ------------------------------------------------------------------- */ TestCase.Messaging = { fail: function(message) { this.manual_failure = message; throw new TestCase.Exception(this, this._last_assertion(), message); }, debug: function(variable) { alert(TestCaseUtil.debug(variable)); }, throw_problem: function(error, message) { throw new TestCase.ProblemException(this, this._last_assertion(), error, message); }, throw_unexp: function(expected, received, message, format) { throw new TestCase.WrongValueException(this, this._last_assertion(), expected, received, message, format); }, _last_assertion: function() { return this['assert_'+this.last_assert_name]; } }; /* ------------------------------------------------------------------- */ TestCase.Assertions = { assert_true: function(value, message) { if (!value) this.throw_unexp(true, value, message); }, assert_false: function(value, message) { if (value) this.throw_unexp(false, value, message); }, assert_null: function(value, message) { if (value != null) this.throw_unexp(null, value, message); }, assert_not_null: function(value, message) { if (value == null) this.throw_problem("The value is null", message); }, assert_type_of: function(type, value, message) { if (typeof(value) != type) this.throw_unexp(type.toLowerCase(), typeof(value), message); }, assert_instance_of: function(type, value, message) { if (!(value instanceof type)) this.throw_unexp(TestCaseUtil.get_class_name(type), TestCaseUtil.get_object_class_name(value), message); }, assert_equal: function(expected, testing, message) { if (!TestCaseUtil.equal(expected, testing)) this.throw_unexp(expected, testing, message, true); }, assert_not_equal: function(expected, testing, message) { if (TestCaseUtil.equal(expected, testing)) this.throw_problem("Received values are equal", message); }, assert_same: function(expected, testing, message) { if (expected != testing) this.throw_problem("Values do not point to the same object", message); }, assert_not_same: function(expected, testing, message) { if (expected == testing) this.throw_problem("Values point to the same object", message); }, assert_match: function(pattern, string, message) { if( !string.match(pattern) ) this.throw_problem("'"+string+"' doesn't match '"+pattern+"'", message); }, assert_not_match: function(pattern, string, message) { if( string.match(pattern) ) this.throw_problem("'"+string+"' match '"+pattern+"'", message); }, assert_throws: function() { var params = this._assert_throw_params.apply(this, arguments); var catched = true; try { params.callback.apply(params.scope_element); catched = false; } catch (e) { catched = e; } if (!catched) this.throw_problem('No exception raised during the call', params.message); else if (params.exception_type) { if (!this._type_of(catched, params.exception_type)) this.throw_unexp("Exception with type of: "+params.exception_type, catched, params.message); } }, assert_nothing_thrown: function() { var params = this._assert_throw_params.apply(this, arguments); try { params.callback.apply(params.scope_element); } catch (e) { if (params.exception_type) { if (this._type_of(e, params.exception_type)) this.throw_problem("Unexpected exception with type '"+params.exception_type+ "' has been raised: \""+e.toString()+"\"", params.message); } else { this.throw_problem('Unexpected exception has been raised: "'+e.toString()+'"', params.message); } } }, _assert_throw_params: function() { var params = typeof arguments[1] == 'function' ? { exception_type: arguments[0], callback: arguments[1]} : { callback: arguments[0]}; params.message = null; for (var i=0; i < arguments.length; i++) if (arguments[i].scope) params.scope_element = arguments[i].scope; if (arguments.length > 1) { for (var i=arguments.length-1; i > 0; i--) { if (arguments[i] == params.callback) { break; } else if (typeof arguments[i] == 'string') { params.message = arguments[i]; } else if (typeof params.scope_element == 'undefined') { params.scope_element = arguments[i]; } } } return params; }, _type_of: function(object, _type) { switch(_type) { case 'number': case Number: return typeof object == 'number'; case 'string': case String: return typeof object == 'string'; default: return object instanceof _type; } }, assert_called: function() { var problems = []; this._execute_wrapped_call(arguments, problems, function(flag, call) { if (!flag) problems.push("Attribute '"+call[1]+"' was not called"); } ); }, assert_not_called: function() { var problems = []; this._execute_wrapped_call( arguments, problems, function(flag, call) { if (flag) problems.push("Attribute '"+call[1]+"' was unexpetedly called"); } ); }, _execute_wrapped_call: function(args, problems, callback) { var params = this._assert_called_params.apply(this, args); try { var flags = []; for (var i=0; i < params.calls.length; i++) { flags[i] = false; this.mock(params.calls[i][0], params.calls[i][1], (function(flags, i, object, name) { var method = object[name]; return function() { flags[i] = true; return method.apply(object, arguments); }; })(flags, i, params.calls[i][0], params.calls[i][1])); } params.callback.apply(params['scope_element']); for (var i=0; i < flags.length; i++) { if (params.calls[i]) callback.apply(this, [flags[i], params.calls[i]]); } } finally { for (var i=0; i < params.calls.length; i++) { this.undo_mock(params.calls[i][0], params.calls[i][1]); } } if (problems.length) { this.throw_problem(problems.join("\n"), params.message); } }, _assert_called_params: function() { var params = { calls: [], callback: null, message: null }; if (typeof arguments[1] == 'string') { params.calls = [[arguments[0], arguments[1]]]; params.callback = arguments[2]; } else if (arguments[0] instanceof Array) { if (arguments[0].length == 2 && typeof(arguments[0][1]) == 'string') { params.calls = [arguments[0]]; } else { params.calls = arguments[0]; } params.callback = arguments[1]; } for (var i=0; i < arguments.length; i++) if (arguments[i].scope) params.scope_element = arguments[i].scope; if (arguments.length > 2) { for (var i=arguments.length-1; i > 0; i--) { if (arguments[i] == params.callback) { break; } else if (typeof arguments[i] == 'string') { params.message = arguments[i]; } else if (typeof params.scope_element == 'undefined') { params.scope_element = arguments[i]; } } } return params; }, assert_exists: function(css_rule, message) { if (TestCaseUtil.$$(css_rule).length == 0) this.throw_problem("Cannot find any element like '"+css_rule+"'", message); }, assert_not_exists: function(css_rule, message) { if (TestCaseUtil.$$(css_rule).length) this.throw_problem("Element '"+css_rule+"' presents on the page", message); }, assert_has_child: function(element, css_rule, message) { if (!TestCaseUtil.Element.down(element, css_rule)) this.throw_problem("Element has no child element like '"+css_rule+"'", message); }, assert_has_no_child: function(element, css_rule, message) { if (TestCaseUtil.Element.down(element, css_rule)) this.throw_problem("Element has child element '"+css_rule+"'", message); }, assert_has_parent: function(element, css_rule, message) { if (!TestCaseUtil.Element.up(element, css_rule)) this.throw_problem("Element has no parent element like '"+css_rule+"'", message); }, assert_has_no_parent: function(element, css_rule, message) { if (TestCaseUtil.Element.up(element, css_rule)) this.throw_problem("Element has parent element '"+css_rule+"'", message); }, assert_visible: function(element_or_css_rule, message) { var elements = typeof element_or_css_rule == 'string' ? TestCaseUtil.$$(element_or_css_rule) : [element_or_css_rule]; if (!elements.length || !elements[0]) this.throw_unexp("Element is visible", "Element is not found", message); else { for (var i=0; i < elements.length; i++) { var element_style = this.__get_element_style(elements[i]); var element_display = elements[i].style.display || element_style['display']; var element_visibility = elements[i].style.visibility || element_style['visibility']; if (element_display == 'none' || element_visibility == 'hidden') this.throw_problem("Element is not visible", message); } } }, assert_hidden: function(element_or_css_rule, message) { var elements = typeof element_or_css_rule == 'string' ? TestCaseUtil.$$(element_or_css_rule) : [element_or_css_rule]; if (!elements.length || !elements[0]) this.throw_unexp("Element is hidden", "Element is not found", message); else { for (var i=0; i < elements.length; i++) { var element_style = this.__get_element_style(elements[i]); var element_display = elements[i].style.display || element_style['display']; var element_visibility = elements[i].style.visibility || element_style['visibility']; if (element_display != 'none' && element_visibility != 'hidden') this.throw_problem("Element is visible", message); } } }, assert_has_class_name: function(element, class_name, message) { if (!TestCaseUtil.Element.has_class_name(element, class_name)) this.throw_problem("Element has no class-name '"+class_name+"'", message); }, assert_has_no_class_name: function(element, class_name, message) { if (TestCaseUtil.Element.has_class_name(element, class_name)) this.throw_problem("Element have class-name '"+class_name+"'", message); }, assert_has_attribute: function(element, name, message) { if (element.getAttribute(name) == null) this.throw_problem("Element has no attribute '"+name+"'", message); }, assert_has_no_attribute: function(element, name, message) { if (element.getAttribute(name) != null) this.throw_problem("Element has attribute '"+name+"'", message); }, assert_style: function(element, style, message) { var element_computed_style = this.__get_element_style(element); var element_style = {}; for (var key in style) { if (key.toLowerCase().substring(key.length-5) == 'color') { var t = document.createElement('span'); t.style[key] = style[key]; style[key] = t.style[key]; } if (element.style && element.style[key]) { element_style[key] = element.style[key]; } else if (element_computed_style[key]) { element_style[key] = element_computed_style[key]; } else { element_style[key] = null; } }; if (!TestCaseUtil.equal(style, element_style)) this.throw_unexp("Element has style:\n "+TestCaseUtil.to_s(style), TestCaseUtil.to_s(element_style), message); }, __get_element_style: function(element) { var style = document.defaultView ? document.defaultView.getComputedStyle(element, null) : element.currentStyle; return style ? style : {}; } }; /* ------------------------------------------------------------------- */ TestCase.AssertionsExtender = { add_assertion: function(name, func) { var name = name.substr(0, 7) == 'assert_' ? name.substr(7, name.length) : name; var assert = {}; assert['assert_'+name] = function() { this._count_assert(name); func.apply(this, arguments); }; TestCaseUtil.extend(this.prototype ? this.prototype : this, assert); TestCaseUtil.extend_with_camelized_aliases(this.prototype ? this.prototype : this, assert); }, add_assertions: function(module) { TestCaseUtil.extend(this.prototype ? this.prototype : this, module); TestCaseUtil.extend_with_camelized_aliases(this.prototype ? this.prototype : this, module); for (var key in module) { if (key.substr(0, 7) == 'assert_') this.add_assertion(key, module[key]); } }, wire_custom_assertions: function(test_def) { for (var name in test_def) { if (name.substr(0, 6) == 'assert' && typeof(test_def[name]) == 'function') { this.add_assertion(name.replace(/([a-z]+)([A-Z])/g, '$1_$2').toLowerCase(), this.prototype[name]); } } }, _count_assert: function(name) { this.assertions++; this.last_assert_name = name; } }; /* ------------------------------------------------------------------- */ TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Starter); TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Fires); TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Mocking); var __mock_aliaces = { mockUp: TestCase.mock, mockup: TestCase.mock, undo_mockup: TestCase.undo_mock, undoMockup: TestCase.undo_mock, undoMockUp: TestCase.undo_mock, with_mocked: TestCase.with_mock, withMocked: TestCase.with_mock, mockupEffects: TestCase.mock_effects, mockUpEffects: TestCase.mock_effects, mockup_effects: TestCase.mock_effects, undo_effects_mockup: TestCase.undo_effects_mock, undoEffectsMockup: TestCase.undo_effects_mock, undoMockUpEffects: TestCase.undo_effects_mock, undoMockupEffects: TestCase.undo_effects_mock, undo_mockup_effects: TestCase.undo_effects_mock, mockUpAjax: TestCase.mock_ajax, mockupAjax: TestCase.mock_ajax, mockup_ajax: TestCase.mock_ajax, undoMockupAjax: TestCase.undo_ajax_mock, undoMockUpAjax: TestCase.undo_ajax_mock, undo_mockup_ajax: TestCase.undo_ajax_mock, undo_ajax_mockup: TestCase.undo_ajax_mock, undoAjaxMockup: TestCase.undo_ajax_mock, undoAjaxMockUp: TestCase.undo_ajax_mock, with_ajax_mockup: TestCase.with_ajax_mock }; TestCaseUtil.extend(TestCase, __mock_aliaces); TestCaseUtil.extend(TestCase.prototype, __mock_aliaces); TestCaseUtil.extend(TestCase.prototype, TestCase.Messaging); TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.AssertionsExtender); TestCase.add_assertions(TestCase.Assertions); TestCaseUtil.extend(TestCase.prototype, { flunk: TestCase.prototype.fail, assert: TestCase.prototype.assert_true, assert_nil: TestCase.prototype.assert_null, assert_not_nil: TestCase.prototype.assert_not_null, assert_kind_of: TestCase.prototype.assert_type_of, assert_no_match: TestCase.prototype.assert_not_match, assertEquals: TestCase.prototype.assert_equal, assertNotEquals: TestCase.prototype.assert_not_equal, assertNoMatch: TestCase.prototype.assert_not_match }); TestCase.prototype.util = TestCaseUtil; /* ------------------------------------------------------------------- */ var TestSuite = TestCaseUtil.create_class({ title: null, reporter: null, test_cases: [], initialize: function() { this.test_cases = []; this.add.apply(this, arguments); }, add: function() { for (var i=0; arguments.length > i; i++) { this.test_cases.push(arguments[i]); } this.test_cases = TestCaseUtil.Array.uniq(this.test_cases); return this; }, remove: function() { for (var i=0; i < arguments.length; i++) this.test_cases = TestCaseUtil.Array.without(this.test_cases, arguments[i]); return this; }, run: function(title) { for (var i=0; i < this.test_cases.length; i++) this.test_cases[i].run(); return this; }, run_on_load: function() { var $this = this; TestCaseUtil.Event.observe(window, 'load', function() { $this.run(); }); } }); TestSuite.prototype.runOnLoad = TestSuite.prototype.run_on_load /* ------------------------------------------------------------------- */ var TestReporter = TestCaseUtil.create_class({ ui_builder: null, report_container: null, current_test: null, current_test_block: null, current_test_done_bar: null, seen_tests: null, initialize: function(ui_builder){ this.ui_builder = ui_builder || new TestReporter.UIBuilder(); this.report_container = this.ui_builder.create_report_container(); this._is_prepared = false; this.seen_tests = []; }, register: function(test) { this._check_prepare(); this.seen_tests.push(test); this.current_test = test; this.current_test_block = this.ui_builder.create_test_block(test, this.report_container); this.current_test_done_bar = this.ui_builder.create_done_bar(this.current_test_block); this.current_test.__start_time = new Date().getTime(); }, test_started: function(test_name) {}, test_passed: function(test_name) { this.current_test_done_bar.style.width = ((this.current_test.passed / this.current_test.size) * 100)+'%'; this.check_for_test_summary_report(); }, test_failed: function(test_name, e, assertion_num) { this.ui_builder.create_error_report( this.current_test_block, test_name, e, assertion_num, this.current_test[test_name].toString() ); this.check_for_test_summary_report(); }, check_for_test_summary_report: function() { if (this.current_test.size == (this.current_test.passed + this.current_test.failed)) this.ui_builder.create_summary_report(this.current_test_block, this.current_test); this.ui_builder.update_overall_summary(this.report_container, this.seen_tests); }, prepare: function() {}, _check_prepare: function() { if(!this._is_prepared){ this.prepare(); this._is_prepared = true; } } }); /* ------------------------------------------------------------------- */ TestReporter.UIBuilder = TestCaseUtil.create_class({ DEFAULT_VIEW_NAME: 'summary-and-failed', initialize: function() {}, create_report_container: function() { var block = document.createElement("DIV"); block.innerHTML = ''+ '
'+ '

Test Report

'+ ' '+ ' '+ '
'; this.init_view_switcher(block); this.init_toggle_all_cases(block); return block; }, create_test_block: function(test, report_container) { var block = document.createElement("LI"); var skipper = document.createElement("INPUT"); skipper.type = "checkbox"; skipper.title = "Skip/Activate Test"; var $this = this; skipper.onclick = function(event) { $this.set_skip_test.apply($this, [event, test, skipper, block]); }; block.appendChild(skipper); skipper.checked = !this.test_is_skipped(test); skipper.disabled = !TestCaseUtil.Cookie.enabled(); if (!skipper.checked) TestCaseUtil.Element.add_class_name(block, 'skipped'); var test_name = document.createElement("DIV"); test_name.className = "test-name"; test_name.innerHTML = test.title; block.appendChild(test_name); TestCaseUtil.Array.find( TestCaseUtil.Element.select(report_container, '*'), function(element){ return element.id == 'test-cases-list';} ).appendChild(block); return block; }, create_done_bar: function(block) { var progress_bar = document.createElement("DIV"); progress_bar.className = "progress-bar"; var done_bar = document.createElement("DIV"); done_bar.className = "done-bar"; progress_bar.appendChild(done_bar); block.appendChild(progress_bar); return done_bar; }, create_error_report: function(test_block, test_name, e, assertion_num, failed_test_code) { var block = document.createElement("DIV"); block.className = 'error-report-case'; block.innerHTML = '
Test error in '+ test_name +' '+ '('+this.get_assertion_name(e, assertion_num)+'):
'+this.create_error_text(e); this.create_error_code_snippet(block, e, assertion_num, failed_test_code); test_block.appendChild(block); test_block.className = 'failed-case'; return block; }, create_error_text: function(e) { if (e instanceof TestCase.WrongValueException) { var e_value = this._htmlized(e.format ? this._formatted_value(e.expected) : e.expected); var r_value = this._htmlized(e.format ? this._formatted_value(e.received) : e.received); return ''+ ' '+ e_value +' '+ ' '+ r_value +' '+ this._create_diff_block(e) + this._create_message_block(e); } else if (e instanceof TestCase.ProblemException) return ' '+this._htmlized(e.problem)+' ' + this._create_message_block(e); else return e.message; }, _htmlized: function(value) { return typeof(value) == 'string' ? value.replace(/\n/g, "
") : value; }, MAX_UNFOLDED_VALUE_LENGTH: 200, _formatted_value: function(value) { var value_s = TestCaseUtil.to_pretty_s(value); if (value_s.length > this.MAX_UNFOLDED_VALUE_LENGTH) { return ""+ value_s.substr(0, this.MAX_UNFOLDED_VALUE_LENGTH).replace(/" + ""+ value_s.replace(/" + 'Full version'; } return ""+ value_s.replace(/"; }, _create_diff_block: function (e) { if (e.format) { try { var is_array = e.expected instanceof Array; var diff = TestCaseUtil.Diff.calc(e.expected, e.received); if (diff.length > 0) { var diff_entries = []; for (var i=0; i < diff.length; i++) { for (var key in diff[i]) {} var value = diff[i][key]; if (typeof e.expected != 'string') { if (typeof e.expected == 'object' && !is_array) { var entries = []; for (var name in value) { entries.push('"'+ name +'": '+TestCaseUtil._one_level_to_s(value[name])); } value = "{"+ entries.join(",\n ") +"\n}"; } else if (typeof e.expected != 'function') { value = TestCaseUtil.debug(value); } } diff_entries.push(''+ value.replace(/').replace(/ /g, ' ') +''); } return ''+ ''+ (is_array ? '[' : '')+ diff_entries.join(is_array ? ',
 ' : '
') + (is_array ? "
]" : '') + '
'+ 'Show'+ '
'; } } catch (e) { } } return ''; }, _create_message_block: function(e) { return e.message ? ' '+e.message+'' : ''; }, create_error_code_snippet: function(error_block, e, assertion_num, failed_test_code) { var failed_test_code = this._paint_source_code(failed_test_code, e, assertion_num); var failed_test_source_popup = document.createElement("DIV"); failed_test_source_popup.className = 'failed-code-snippet'; failed_test_source_popup.innerHTML = ''+failed_test_code+''; failed_test_source_popup.style.display = 'none'; var test_name_pointer = TestCaseUtil.Element.select(error_block, 'span.test-name')[0]; test_name_pointer.parentNode.insertBefore(failed_test_source_popup, test_name_pointer); TestCaseUtil.Event.observe(failed_test_source_popup, 'click', function(event) { TestCaseUtil.Event.stop(event); }); TestCaseUtil.Event.observe(test_name_pointer, 'click', function(event) { TestCaseUtil.Event.stop(event); TestCaseUtil.Element.toggle(failed_test_source_popup); }); TestCaseUtil.Event.observe(document, 'click', function() { TestCaseUtil.Element.hide(failed_test_source_popup); }); return failed_test_source_popup; }, _paint_source_code: function(failed_test_code, e, assertion_num) { if (TestCaseUtil.Browser.IE) failed_test_code = failed_test_code.replace(/\n/mg, "
\n").replace(/ /mg, " "); var assertion_num = assertion_num - 1; var asserts_counter = 0; var assertion_matches = failed_test_code.match(/this\.assert.+(\n|;)/ig); if (assertion_matches) { for (var i=0; i < assertion_matches.length; i++) { match = assertion_matches[i]; asserts_counter ++; if (asserts_counter == assertion_num) failed_test_code = failed_test_code.replace(match, ''+match+''); } failed_test_code = failed_test_code.replace(/(this)(\.)(assert[^\(]*)(\s*\()/img, '$1$2$3$4'); } return failed_test_code; }, create_summary_report: function(test_block, test) { test.__taken_seconds = (test.__start_time && !test.skipped) ? (new Date().getTime() - test.__start_time)/1000 : 0; var block = document.createElement("DIV"); block.className = 'error-report-summary'; block.innerHTML = "Total tests: "+ test.size + " / Passed: "+ test.passed + " / Failed: "+ test.failed + " / Assertions: "+ test.assertions + " / Seconds: "+test.__taken_seconds; test_block.appendChild(block); return block; }, update_overall_summary: function(block, cases_list) { var cases = 0, tests = 0, passed = 0, failed = 0, assertions = 0, seconds = 0; for (var i=0; i < cases_list.length; i++) { var testcase = cases_list[i]; if (!testcase.skipped) { cases++; tests += testcase.size; passed += testcase.passed; failed += testcase.failed; assertions += testcase.assertions; seconds += testcase.__taken_seconds; } } var list_block = TestCaseUtil.Element.select(block, 'li#testcase-summary-block')[0]; if (list_block) { var done_bar_element = TestCaseUtil.Element.select(list_block, 'div.done-bar')[0]; if (done_bar_element) done_bar_element.style.width = ((tests > 0 ? (passed / tests) : 0) * 100)+'%'; var summary_element = TestCaseUtil.Element.select(list_block, 'div.error-report-summary')[0]; if (summary_element) summary_element.innerHTML = "Total Cases: "+cases+ " / Tests: "+tests+ " / Passed: "+passed+ " / Failed : "+failed+ " / Assertions : "+assertions+ " / Seconds: "+seconds.toString().substr(0,5); } if (cases > 0) { var switcher = TestCaseUtil.Element.select(block, 'input#testcase-toggle-all-cases')[0]; if (switcher) switcher.checked = true; } }, test_is_skipped: function(test) { return TestCaseUtil.Array.include(this.get_skipped_cases(), escape(test.title)); }, get_assertion_name: function(e, assertion_num) { if (e.test && e.assertion) { for (key in e.test) if (e.test[key] == e.assertion) return key; } else { return "assertion #"+assertion_num; } }, init_view_switcher: function(block) { var descendants = TestCaseUtil.Element.select(block, '*'); var switcher = TestCaseUtil.Array.find(descendants, function(el) { return el.id == 'testcase-view-switcher' }); var list_element = TestCaseUtil.Array.find(descendants, function(el) { return el.id == 'test-cases-list' }); if (switcher && list_element) { TestCaseUtil.Event.observe(switcher, 'change', function() { list_element.className = switcher.value; TestCaseUtil.Cookie.set("testcase_view", switcher.value, 100); }); switcher.value = this.get_current_view_name(); list_element.className = switcher.value; } }, get_current_view_name: function() { var view_name = TestCaseUtil.Cookie.get('testcase_view'); return view_name == '' ? this.DEFAULT_VIEW_NAME : view_name; }, init_toggle_all_cases: function(block) { var checkbox = TestCaseUtil.Array.find(TestCaseUtil.Element.select(block, '*'), function(el) { return el.id == 'testcase-toggle-all-cases' }); if (checkbox) { var $this = this; TestCaseUtil.Event.observe(checkbox, 'click', function(event) { $this.toggle_all_cases.apply($this, [this, block, checkbox]); }); checkbox.checked = false; checkbox.disabled = TestCaseUtil.Cookie.enabled() ? false : true; } }, toggle_all_cases: function(event, block, checkbox) { var elements = TestCaseUtil.Element.select(block, 'ul li input[type="checkbox"]'); for (var i=0; i < elements.length; i++) { elements[i].checked = checkbox.checked; if (elements[i].onclick) elements[i].onclick(); } }, set_skip_test: function(event, test, checkbox, block) { TestCaseUtil.Element[checkbox.checked ? 'remove_class_name' : 'add_class_name'](block, 'skipped'); var skipped_cases = this.get_skipped_cases(); if (checkbox.checked) skipped_cases = TestCaseUtil.Array.without(skipped_cases, escape(test.title)); else skipped_cases.push(escape(test.title)); TestCaseUtil.Cookie.set("testcase_skipped_cases", skipped_cases.join("|")); }, get_skipped_cases: function() { return TestCaseUtil.Cookie.get('testcase_skipped_cases').split("|"); } }); /* ------------------------------------------------------------------- */ TestReporter.InlineReporter = TestCaseUtil.create_class(TestReporter, { prepare: function() { var source = ''+ '
'+ '
'+ '
x
'+ '
'+ '
'+ '
'+ ''; if (document.body) { var block = document.createElement("DIV"); block.innerHTML = source; document.body.appendChild(block); } else { document.write(source); } TestCaseUtil.Element.down('testcase-inline-report-block', 'div.body').appendChild(this.report_container); } }); /* ------------------------------------------------------------------- */ TestCaseUtil.extend(TestCase, { reporter: new TestReporter.InlineReporter() }); /* ---------------- THE STYLES COLLECTION DUMPING ------------------- */ document.write('');