<!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>Prototype Unit test file</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <script src="../../dist/prototype.js" type="text/javascript"></script> <script src="../lib/unittest.js" type="text/javascript"></script> <link rel="stylesheet" href="../test.css" type="text/css" /> <style type="text/css" media="screen"> /* <![CDATA[ */ #testcss1 { font-size:11px; color: #f00; } #testcss2 { font-size:12px; color: #0f0; display: none; } /* ]]> */ </style> </head> <body> <h1>Prototype Unit test file</h1> <p> Test of utility functions in base.js </p> <!-- Log output --> <div id="testlog"> </div> <div id="test"></div> <ul id="list"> <li></li> <li></li> <li></li> </ul> <!-- Tests follow --> <script type="text/javascript" language="javascript" charset="utf-8"> // <![CDATA[ var Person = function(name){ this.name = name; }; Person.prototype.toJSON = function() { return '-' + this.name; }; var arg1 = 1; var arg2 = 2; var arg3 = 3; function TestObj() { }; TestObj.prototype.assertingEventHandler = function(event, assertEvent, assert1, assert2, assert3, a1, a2, a3) { assertEvent(event); assert1(a1); assert2(a2); assert3(a3); }; var globalBindTest = null; // base class var Animal = Class.create({ initialize: function(name) { this.name = name; }, name: "", eat: function() { return this.say("Yum!"); }, say: function(message) { return this.name + ": " + message; } }); // subclass that augments a method var Cat = Class.create(Animal, { eat: function($super, food) { if (food instanceof Mouse) return $super(); else return this.say("Yuk! I only eat mice."); } }); // empty subclass var Mouse = Class.create(Animal, {}); //mixins var Sellable = { getValue: function(pricePerKilo) { return this.weight * pricePerKilo; }, inspect: function() { return '#<Sellable: #{weight}kg>'.interpolate(this); } }; var Reproduceable = { reproduce: function(partner) { if (partner.constructor != this.constructor || partner.sex == this.sex) return null; var weight = this.weight / 10, sex = Math.random(1).round() ? 'male' : 'female'; return new this.constructor('baby', weight, sex); } }; // base class with mixin var Plant = Class.create(Sellable, { initialize: function(name, weight) { this.name = name; this.weight = weight; }, inspect: function() { return '#<Plant: #{name}>'.interpolate(this); } }); // subclass with mixin var Dog = Class.create(Animal, Reproduceable, { initialize: function($super, name, weight, sex) { this.weight = weight; this.sex = sex; $super(name); } }); // subclass with mixins var Ox = Class.create(Animal, Sellable, Reproduceable, { initialize: function($super, name, weight, sex) { this.weight = weight; this.sex = sex; $super(name); }, eat: function(food) { if (food instanceof Plant) this.weight += food.weight; }, inspect: function() { return '#<Ox: #{name}>'.interpolate(this); } }); new Test.Unit.Runner({ testFunctionArgumentNames: function() { with(this) { assertEnumEqual([], (function() {}).argumentNames()); assertEnumEqual(["one"], (function(one) {}).argumentNames()); assertEnumEqual(["one", "two", "three"], (function(one, two, three) {}).argumentNames()); assertEqual("$super", (function($super) {}).argumentNames().first()); function named1() {}; assertEnumEqual([], named1.argumentNames()); function named2(one) {}; assertEnumEqual(["one"], named2.argumentNames()); function named3(one, two, three) {}; assertEnumEqual(["one", "two", "three"], named3.argumentNames()); }}, testFunctionBind: function() { with(this) { function methodWithoutArguments() { return this.hi }; function methodWithArguments() { return this.hi + ',' + $A(arguments).join(',') }; var func = Prototype.emptyFunction; assertIdentical(func, func.bind()); assertIdentical(func, func.bind(undefined)); assertNotIdentical(func, func.bind(null)); assertEqual('without', methodWithoutArguments.bind({ hi: 'without' })()); assertEqual('with,arg1,arg2', methodWithArguments.bind({ hi: 'with' })('arg1','arg2')); assertEqual('withBindArgs,arg1,arg2', methodWithArguments.bind({ hi: 'withBindArgs' }, 'arg1', 'arg2')()); assertEqual('withBindArgsAndArgs,arg1,arg2,arg3,arg4', methodWithArguments.bind({ hi: 'withBindArgsAndArgs' }, 'arg1', 'arg2')('arg3', 'arg4')); }}, testFunctionCurry: function() { with(this) { var split = function(delimiter, string) { return string.split(delimiter); }; var splitOnColons = split.curry(":"); assertNotIdentical(split, splitOnColons); assertEnumEqual(split(":", "0:1:2:3:4:5"), splitOnColons("0:1:2:3:4:5")); assertIdentical(split, split.curry()); }}, testFunctionDelay: function() { with(this) { window.delayed = undefined; var delayedFunction = function() { window.delayed = true; }; var delayedFunctionWithArgs = function() { window.delayedWithArgs = $A(arguments).join(' '); }; delayedFunction.delay(0.8); delayedFunctionWithArgs.delay(0.8, 'hello', 'world'); assertUndefined(window.delayed); wait(1000, function() { assert(window.delayed); assertEqual('hello world', window.delayedWithArgs); }); }}, testFunctionWrap: function() { with(this) { function sayHello(){ return 'hello world'; }; assertEqual('HELLO WORLD', sayHello.wrap(function(proceed) { return proceed().toUpperCase(); })()); var temp = String.prototype.capitalize; String.prototype.capitalize = String.prototype.capitalize.wrap(function(proceed, eachWord) { if(eachWord && this.include(' ')) return this.split(' ').map(function(str){ return str.capitalize(); }).join(' '); return proceed(); }); assertEqual('Hello world', 'hello world'.capitalize()); assertEqual('Hello World', 'hello world'.capitalize(true)); assertEqual('Hello', 'hello'.capitalize()); String.prototype.capitalize = temp; }}, testFunctionDefer: function() { with(this) { window.deferred = undefined; var deferredFunction = function() { window.deferred = true; }; deferredFunction.defer(); assertUndefined(window.deferred); wait(50, function() { assert(window.deferred); window.deferredValue = 0; var deferredFunction2 = function(arg) { window.deferredValue = arg; }; deferredFunction2.defer('test'); wait(50, function() { assertEqual('test', window.deferredValue); }); }); }}, testFunctionMethodize: function() { with(this) { var Foo = { bar: function(baz) { return baz } }; var baz = { quux: Foo.bar.methodize() }; assertEqual(Foo.bar.methodize(), baz.quux); assertEqual(baz, Foo.bar(baz)); assertEqual(baz, baz.quux()); }}, testObjectExtend: function() { with(this) { var object = {foo: 'foo', bar: [1, 2, 3]}; assertIdentical(object, Object.extend(object)); assertHashEqual({foo: 'foo', bar: [1, 2, 3]}, object); assertIdentical(object, Object.extend(object, {bla: 123})); assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: 123}, object); assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: null}, Object.extend(object, {bla: null})); }}, testObjectToQueryString: function() { with(this) { assertEqual('a=A&b=B&c=C&d=D%23', Object.toQueryString({a: 'A', b: 'B', c: 'C', d: 'D#'})); }}, testObjectClone: function() { with(this) { var object = {foo: 'foo', bar: [1, 2, 3]}; assertNotIdentical(object, Object.clone(object)); assertHashEqual(object, Object.clone(object)); assertHashEqual({}, Object.clone()); var clone = Object.clone(object); delete clone.bar; assertHashEqual({foo: 'foo'}, clone, "Optimizing Object.clone perf using prototyping doesn't allow properties to be deleted."); }}, testObjectInspect: function() { with(this) { assertEqual('undefined', Object.inspect()); assertEqual('undefined', Object.inspect(undefined)); assertEqual('null', Object.inspect(null)); assertEqual("'foo\\\\b\\\'ar'", Object.inspect('foo\\b\'ar')); assertEqual('[]', Object.inspect([])); assertNothingRaised(function() { Object.inspect(window.Node) }); }}, testObjectToJSON: function() { with(this) { assertUndefined(Object.toJSON(undefined)); assertUndefined(Object.toJSON(Prototype.K)); assertEqual('\"\"', Object.toJSON('')); assertEqual('[]', Object.toJSON([])); assertEqual('[\"a\"]', Object.toJSON(['a'])); assertEqual('[\"a\", 1]', Object.toJSON(['a', 1])); assertEqual('[\"a\", {\"b\": null}]', Object.toJSON(['a', {'b': null}])); assertEqual('{\"a\": \"hello!\"}', Object.toJSON({a: 'hello!'})); assertEqual('{}', Object.toJSON({})); assertEqual('{}', Object.toJSON({a: undefined, b: undefined, c: Prototype.K})); assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}', Object.toJSON({'b': [undefined, false, true, undefined], c: {a: 'hello!'}})); assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}', Object.toJSON($H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}))); assertEqual('true', Object.toJSON(true)); assertEqual('false', Object.toJSON(false)); assertEqual('null', Object.toJSON(null)); var sam = new Person('sam'); assertEqual('-sam', Object.toJSON(sam)); assertEqual('-sam', sam.toJSON()); var element = $('test'); assertUndefined(Object.toJSON(element)); element.toJSON = function(){return 'I\'m a div with id test'}; assertEqual('I\'m a div with id test', Object.toJSON(element)); }}, testObjectToHTML: function() { with(this) { assertIdentical('', Object.toHTML()); assertIdentical('', Object.toHTML('')); assertIdentical('', Object.toHTML(null)); assertIdentical('0', Object.toHTML(0)); assertIdentical('123', Object.toHTML(123)); assertEqual('hello world', Object.toHTML('hello world')); assertEqual('hello world', Object.toHTML({toHTML: function() { return 'hello world' }})); }}, testObjectIsArray: function() { with(this) { assert(Object.isArray([])); assert(Object.isArray([0])); assert(Object.isArray([0, 1])); assert(!Object.isArray({})); assert(!Object.isArray($('list').childNodes)); assert(!Object.isArray()); assert(!Object.isArray('')); assert(!Object.isArray('foo')); assert(!Object.isArray(0)); assert(!Object.isArray(1)); assert(!Object.isArray(null)); assert(!Object.isArray(true)); assert(!Object.isArray(false)); assert(!Object.isArray(undefined)); }}, testObjectIsHash: function() { with(this) { assert(Object.isHash($H())); assert(Object.isHash(new Hash())); assert(!Object.isHash({})); }}, testObjectIsElement: function() { with(this) { assert(Object.isElement(document.createElement('div'))); assert(Object.isElement(new Element('div'))); assert(Object.isElement($('testlog'))); assert(!Object.isElement(document.createTextNode('bla'))); }}, testObjectIsFunction: function() { with(this) { assert(Object.isFunction(function() { })); assert(Object.isFunction(Class.create())); assert(!Object.isFunction("a string")); assert(!Object.isFunction($("testlog"))); assert(!Object.isFunction([])); assert(!Object.isFunction({})); assert(!Object.isFunction(0)); assert(!Object.isFunction(false)); assert(!Object.isFunction(undefined)); }}, testObjectIsString: function() { with(this) { assert(!Object.isString(function() { })); assert(Object.isString("a string")); assert(!Object.isString(0)); assert(!Object.isString([])); assert(!Object.isString({})); assert(!Object.isString(false)); assert(!Object.isString(undefined)); }}, testObjectIsNumber: function() { with(this) { assert(Object.isNumber(0)); assert(Object.isNumber(1.0)); assert(!Object.isNumber(function() { })); assert(!Object.isNumber("a string")); assert(!Object.isNumber([])); assert(!Object.isNumber({})); assert(!Object.isNumber(false)); assert(!Object.isNumber(undefined)); }}, testObjectIsUndefined: function() { with(this) { assert(Object.isUndefined(undefined)); assert(!Object.isUndefined(null)); assert(!Object.isUndefined(false)); assert(!Object.isUndefined(0)); assert(!Object.isUndefined("")); assert(!Object.isUndefined(function() { })); assert(!Object.isUndefined([])); assert(!Object.isUndefined({})); }}, // sanity check testDoesntExtendObjectPrototype: function() {with(this) { // for-in is supported with objects var iterations = 0, obj = { a: 1, b: 2, c: 3 }; for(property in obj) iterations++; assertEqual(3, iterations); // for-in is not supported with arrays iterations = 0; var arr = [1,2,3]; for(property in arr) iterations++; assert(iterations > 3); }}, testPeriodicalExecuterStop: function() {with(this) { var peEventCount = 0; function peEventFired(pe) { if (++peEventCount > 2) pe.stop(); } // peEventFired will stop the PeriodicalExecuter after 3 callbacks new PeriodicalExecuter(peEventFired, 0.05); wait(600, function() { assertEqual(3, peEventCount); }); }}, testBindAsEventListener: function() { for( var i = 0; i < 10; ++i ){ var div = document.createElement('div'); div.setAttribute('id','test-'+i); document.body.appendChild(div); var tobj = new TestObj(); var eventTest = { test: true }; var call = tobj.assertingEventHandler.bindAsEventListener(tobj, this.assertEqual.bind(this, eventTest), this.assertEqual.bind(this, arg1), this.assertEqual.bind(this, arg2), this.assertEqual.bind(this, arg3), arg1, arg2, arg3 ); call(eventTest); } }, testDateToJSON: function() {with(this) { assertEqual('\"1970-01-01T00:00:00Z\"', new Date(Date.UTC(1970, 0, 1)).toJSON()); }}, testRegExpEscape: function() {with(this) { assertEqual('word', RegExp.escape('word')); assertEqual('\\/slashes\\/', RegExp.escape('/slashes/')); assertEqual('\\\\backslashes\\\\', RegExp.escape('\\backslashes\\')); assertEqual('\\\\border of word', RegExp.escape('\\border of word')); assertEqual('\\(\\?\\:non-capturing\\)', RegExp.escape('(?:non-capturing)')); assertEqual('non-capturing', new RegExp(RegExp.escape('(?:') + '([^)]+)').exec('(?:non-capturing)')[1]); assertEqual('\\(\\?\\=positive-lookahead\\)', RegExp.escape('(?=positive-lookahead)')); assertEqual('positive-lookahead', new RegExp(RegExp.escape('(?=') + '([^)]+)').exec('(?=positive-lookahead)')[1]); assertEqual('\\(\\?<\\=positive-lookbehind\\)', RegExp.escape('(?<=positive-lookbehind)')); assertEqual('positive-lookbehind', new RegExp(RegExp.escape('(?<=') + '([^)]+)').exec('(?<=positive-lookbehind)')[1]); assertEqual('\\(\\?\\!negative-lookahead\\)', RegExp.escape('(?!negative-lookahead)')); assertEqual('negative-lookahead', new RegExp(RegExp.escape('(?!') + '([^)]+)').exec('(?!negative-lookahead)')[1]); assertEqual('\\(\\?<\\!negative-lookbehind\\)', RegExp.escape('(?<!negative-lookbehind)')); assertEqual('negative-lookbehind', new RegExp(RegExp.escape('(?<!') + '([^)]+)').exec('(?<!negative-lookbehind)')[1]); assertEqual('\\[\\\\w\\]\\+', RegExp.escape('[\\w]+')); assertEqual('character class', new RegExp(RegExp.escape('[') + '([^\\]]+)').exec('[character class]')[1]); assertEqual('<div>', new RegExp(RegExp.escape('<div>')).exec('<td><div></td>')[0]); assertEqual('false', RegExp.escape(false)); assertEqual('undefined', RegExp.escape()); assertEqual('null', RegExp.escape(null)); assertEqual('42', RegExp.escape(42)); assertEqual('\\\\n\\\\r\\\\t', RegExp.escape('\\n\\r\\t')); assertEqual('\n\r\t', RegExp.escape('\n\r\t')); assertEqual('\\{5,2\\}', RegExp.escape('{5,2}')); assertEqual( '\\/\\(\\[\\.\\*\\+\\?\\^\\=\\!\\:\\$\\{\\}\\(\\)\\|\\[\\\\\\]\\\\\\\/\\\\\\\\\\]\\)\\/g', RegExp.escape('/([.*+?^=!:${}()|[\\]\\/\\\\])/g') ); }}, testBrowserDetection: function() {with(this) { var results = $H(Prototype.Browser).map(function(engine){ return engine; }).partition(function(engine){ return engine[1] === true }); var trues = results[0], falses = results[1]; info('User agent string is: ' + navigator.userAgent); assert(trues.size() == 0 || trues.size() == 1, 'There should be only one or no browser detected.'); // we should have definite trues or falses here trues.each(function(result){ assert(result[1] === true); }); falses.each(function(result){ assert(result[1] === false); }); if(navigator.userAgent.indexOf('AppleWebKit/') > -1) { info('Running on WebKit'); assert(Prototype.Browser.WebKit); } if(!!window.opera) { info('Running on Opera'); assert(Prototype.Browser.Opera); } if(!!(window.attachEvent && !window.opera)) { info('Running on IE'); assert(Prototype.Browser.IE); } if(navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1) { info('Running on Gecko'); assert(Prototype.Browser.Gecko); } }}, testClassCreate: function() { with(this) { assert(Object.isFunction(Animal), 'Animal is not a constructor'); assertEnumEqual([Cat, Mouse, Dog, Ox], Animal.subclasses); Animal.subclasses.each(function(subclass) { assertEqual(Animal, subclass.superclass); }); var Bird = Class.create(Animal); assertEqual(Bird, Animal.subclasses.last()); // for..in loop (for some reason) doesn't iterate over the constructor property in top-level classes assertEnumEqual(Object.keys(new Animal).sort(), Object.keys(new Bird).without('constructor').sort()); }}, testClassInstantiation: function() { with(this) { var pet = new Animal("Nibbles"); assertEqual("Nibbles", pet.name, "property not initialized"); assertEqual('Nibbles: Hi!', pet.say('Hi!')); assertEqual(Animal, pet.constructor, "bad constructor reference"); assertUndefined(pet.superclass); var Empty = Class.create(); assert('object', typeof new Empty); }}, testInheritance: function() { with(this) { var tom = new Cat('Tom'); assertEqual(Cat, tom.constructor, "bad constructor reference"); assertEqual(Animal, tom.constructor.superclass, 'bad superclass reference'); assertEqual('Tom', tom.name); assertEqual('Tom: meow', tom.say('meow')); assertEqual('Tom: Yuk! I only eat mice.', tom.eat(new Animal)); }}, testSuperclassMethodCall: function() { with(this) { var tom = new Cat('Tom'); assertEqual('Tom: Yum!', tom.eat(new Mouse)); // augment the constructor and test var Dodo = Class.create(Animal, { initialize: function($super, name) { $super(name); this.extinct = true; }, say: function($super, message) { return $super(message) + " honk honk"; } }); var gonzo = new Dodo('Gonzo'); assertEqual('Gonzo', gonzo.name); assert(gonzo.extinct, 'Dodo birds should be extinct'); assertEqual("Gonzo: hello honk honk", gonzo.say("hello")); }}, testClassAddMethods: function() { with(this) { var tom = new Cat('Tom'); var jerry = new Mouse('Jerry'); Animal.addMethods({ sleep: function() { return this.say('ZZZ'); } }); Mouse.addMethods({ sleep: function($super) { return $super() + " ... no, can't sleep! Gotta steal cheese!"; }, escape: function(cat) { return this.say('(from a mousehole) Take that, ' + cat.name + '!'); } }); assertEqual('Tom: ZZZ', tom.sleep(), "added instance method not available to subclass"); assertEqual("Jerry: ZZZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep()); assertEqual("Jerry: (from a mousehole) Take that, Tom!", jerry.escape(tom)); // insure that a method has not propagated *up* the prototype chain: assertUndefined(tom.escape); assertUndefined(new Animal().escape); Animal.addMethods({ sleep: function() { return this.say('zZzZ'); } }); assertEqual("Jerry: zZzZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep()); }}, testBaseClassWithMixin: function() { with(this) { var grass = new Plant('grass', 3); assertRespondsTo('getValue', grass); assertEqual('#<Plant: grass>', grass.inspect()); }}, testSubclassWithMixin: function() { with(this) { var snoopy = new Dog('Snoopy', 12, 'male'); assertRespondsTo('reproduce', snoopy); }}, testSubclassWithMixins: function() { with(this) { var cow = new Ox('cow', 400, 'female'); assertEqual('#<Ox: cow>', cow.inspect()); assertRespondsTo('reproduce', cow); assertRespondsTo('getValue', cow); }}, testClassWithToStringAndValueOfMethods: function() { with(this) { var Foo = Class.create({ toString: function() { return "toString"; }, valueOf: function() { return "valueOf"; } }); assertEqual("toString", new Foo().toString()); assertEqual("valueOf", new Foo().valueOf()); }} }); // ]]> </script> </body> </html>