Basic PHP document management system, including automatic detection of corporate logos in letters
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

unittest.js 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. // script.aculo.us unittest.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
  2. // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
  3. // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
  4. // (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/)
  5. //
  6. // script.aculo.us is freely distributable under the terms of an MIT-style license.
  7. // For details, see the script.aculo.us web site: http://script.aculo.us/
  8. // experimental, Firefox-only
  9. Event.simulateMouse = function(element, eventName) {
  10. var options = Object.extend({
  11. pointerX: 0,
  12. pointerY: 0,
  13. buttons: 0,
  14. ctrlKey: false,
  15. altKey: false,
  16. shiftKey: false,
  17. metaKey: false
  18. }, arguments[2] || {});
  19. var oEvent = document.createEvent("MouseEvents");
  20. oEvent.initMouseEvent(eventName, true, true, document.defaultView,
  21. options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
  22. options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));
  23. if(this.mark) Element.remove(this.mark);
  24. this.mark = document.createElement('div');
  25. this.mark.appendChild(document.createTextNode(" "));
  26. document.body.appendChild(this.mark);
  27. this.mark.style.position = 'absolute';
  28. this.mark.style.top = options.pointerY + "px";
  29. this.mark.style.left = options.pointerX + "px";
  30. this.mark.style.width = "5px";
  31. this.mark.style.height = "5px;";
  32. this.mark.style.borderTop = "1px solid red;"
  33. this.mark.style.borderLeft = "1px solid red;"
  34. if(this.step)
  35. alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
  36. $(element).dispatchEvent(oEvent);
  37. };
  38. // Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
  39. // You need to downgrade to 1.0.4 for now to get this working
  40. // See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
  41. Event.simulateKey = function(element, eventName) {
  42. var options = Object.extend({
  43. ctrlKey: false,
  44. altKey: false,
  45. shiftKey: false,
  46. metaKey: false,
  47. keyCode: 0,
  48. charCode: 0
  49. }, arguments[2] || {});
  50. var oEvent = document.createEvent("KeyEvents");
  51. oEvent.initKeyEvent(eventName, true, true, window,
  52. options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
  53. options.keyCode, options.charCode );
  54. $(element).dispatchEvent(oEvent);
  55. };
  56. Event.simulateKeys = function(element, command) {
  57. for(var i=0; i<command.length; i++) {
  58. Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
  59. }
  60. };
  61. var Test = {}
  62. Test.Unit = {};
  63. // security exception workaround
  64. Test.Unit.inspect = Object.inspect;
  65. Test.Unit.Logger = Class.create();
  66. Test.Unit.Logger.prototype = {
  67. initialize: function(log) {
  68. this.log = $(log);
  69. if (this.log) {
  70. this._createLogTable();
  71. }
  72. },
  73. start: function(testName) {
  74. if (!this.log) return;
  75. this.testName = testName;
  76. this.lastLogLine = document.createElement('tr');
  77. this.statusCell = document.createElement('td');
  78. this.nameCell = document.createElement('td');
  79. this.nameCell.className = "nameCell";
  80. this.nameCell.appendChild(document.createTextNode(testName));
  81. this.messageCell = document.createElement('td');
  82. this.lastLogLine.appendChild(this.statusCell);
  83. this.lastLogLine.appendChild(this.nameCell);
  84. this.lastLogLine.appendChild(this.messageCell);
  85. this.loglines.appendChild(this.lastLogLine);
  86. },
  87. finish: function(status, summary) {
  88. if (!this.log) return;
  89. this.lastLogLine.className = status;
  90. this.statusCell.innerHTML = status;
  91. this.messageCell.innerHTML = this._toHTML(summary);
  92. this.addLinksToResults();
  93. },
  94. message: function(message) {
  95. if (!this.log) return;
  96. this.messageCell.innerHTML = this._toHTML(message);
  97. },
  98. summary: function(summary) {
  99. if (!this.log) return;
  100. this.logsummary.innerHTML = this._toHTML(summary);
  101. },
  102. _createLogTable: function() {
  103. this.log.innerHTML =
  104. '<div id="logsummary"></div>' +
  105. '<table id="logtable">' +
  106. '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
  107. '<tbody id="loglines"></tbody>' +
  108. '</table>';
  109. this.logsummary = $('logsummary')
  110. this.loglines = $('loglines');
  111. },
  112. _toHTML: function(txt) {
  113. return txt.escapeHTML().replace(/\n/g,"<br/>");
  114. },
  115. addLinksToResults: function(){
  116. $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log
  117. td.title = "Run only this test"
  118. Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;});
  119. });
  120. $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log
  121. td.title = "Run all tests"
  122. Event.observe(td, 'click', function(){ window.location.search = "";});
  123. });
  124. }
  125. }
  126. Test.Unit.Runner = Class.create();
  127. Test.Unit.Runner.prototype = {
  128. initialize: function(testcases) {
  129. this.options = Object.extend({
  130. testLog: 'testlog'
  131. }, arguments[1] || {});
  132. this.options.resultsURL = this.parseResultsURLQueryParameter();
  133. this.options.tests = this.parseTestsQueryParameter();
  134. if (this.options.testLog) {
  135. this.options.testLog = $(this.options.testLog) || null;
  136. }
  137. if(this.options.tests) {
  138. this.tests = [];
  139. for(var i = 0; i < this.options.tests.length; i++) {
  140. if(/^test/.test(this.options.tests[i])) {
  141. this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
  142. }
  143. }
  144. } else {
  145. if (this.options.test) {
  146. this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
  147. } else {
  148. this.tests = [];
  149. for(var testcase in testcases) {
  150. if(/^test/.test(testcase)) {
  151. this.tests.push(
  152. new Test.Unit.Testcase(
  153. this.options.context ? ' -> ' + this.options.titles[testcase] : testcase,
  154. testcases[testcase], testcases["setup"], testcases["teardown"]
  155. ));
  156. }
  157. }
  158. }
  159. }
  160. this.currentTest = 0;
  161. this.logger = new Test.Unit.Logger(this.options.testLog);
  162. setTimeout(this.runTests.bind(this), 1000);
  163. },
  164. parseResultsURLQueryParameter: function() {
  165. return window.location.search.parseQuery()["resultsURL"];
  166. },
  167. parseTestsQueryParameter: function(){
  168. if (window.location.search.parseQuery()["tests"]){
  169. return window.location.search.parseQuery()["tests"].split(',');
  170. };
  171. },
  172. // Returns:
  173. // "ERROR" if there was an error,
  174. // "FAILURE" if there was a failure, or
  175. // "SUCCESS" if there was neither
  176. getResult: function() {
  177. var hasFailure = false;
  178. for(var i=0;i<this.tests.length;i++) {
  179. if (this.tests[i].errors > 0) {
  180. return "ERROR";
  181. }
  182. if (this.tests[i].failures > 0) {
  183. hasFailure = true;
  184. }
  185. }
  186. if (hasFailure) {
  187. return "FAILURE";
  188. } else {
  189. return "SUCCESS";
  190. }
  191. },
  192. postResults: function() {
  193. if (this.options.resultsURL) {
  194. new Ajax.Request(this.options.resultsURL,
  195. { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
  196. }
  197. },
  198. runTests: function() {
  199. var test = this.tests[this.currentTest];
  200. if (!test) {
  201. // finished!
  202. this.postResults();
  203. this.logger.summary(this.summary());
  204. return;
  205. }
  206. if(!test.isWaiting) {
  207. this.logger.start(test.name);
  208. }
  209. test.run();
  210. if(test.isWaiting) {
  211. this.logger.message("Waiting for " + test.timeToWait + "ms");
  212. setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
  213. } else {
  214. this.logger.finish(test.status(), test.summary());
  215. this.currentTest++;
  216. // tail recursive, hopefully the browser will skip the stackframe
  217. this.runTests();
  218. }
  219. },
  220. summary: function() {
  221. var assertions = 0;
  222. var failures = 0;
  223. var errors = 0;
  224. var messages = [];
  225. for(var i=0;i<this.tests.length;i++) {
  226. assertions += this.tests[i].assertions;
  227. failures += this.tests[i].failures;
  228. errors += this.tests[i].errors;
  229. }
  230. return (
  231. (this.options.context ? this.options.context + ': ': '') +
  232. this.tests.length + " tests, " +
  233. assertions + " assertions, " +
  234. failures + " failures, " +
  235. errors + " errors");
  236. }
  237. }
  238. Test.Unit.Assertions = Class.create();
  239. Test.Unit.Assertions.prototype = {
  240. initialize: function() {
  241. this.assertions = 0;
  242. this.failures = 0;
  243. this.errors = 0;
  244. this.messages = [];
  245. },
  246. summary: function() {
  247. return (
  248. this.assertions + " assertions, " +
  249. this.failures + " failures, " +
  250. this.errors + " errors" + "\n" +
  251. this.messages.join("\n"));
  252. },
  253. pass: function() {
  254. this.assertions++;
  255. },
  256. fail: function(message) {
  257. this.failures++;
  258. this.messages.push("Failure: " + message);
  259. },
  260. info: function(message) {
  261. this.messages.push("Info: " + message);
  262. },
  263. error: function(error) {
  264. this.errors++;
  265. this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
  266. },
  267. status: function() {
  268. if (this.failures > 0) return 'failed';
  269. if (this.errors > 0) return 'error';
  270. return 'passed';
  271. },
  272. assert: function(expression) {
  273. var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
  274. try { expression ? this.pass() :
  275. this.fail(message); }
  276. catch(e) { this.error(e); }
  277. },
  278. assertEqual: function(expected, actual) {
  279. var message = arguments[2] || "assertEqual";
  280. try { (expected == actual) ? this.pass() :
  281. this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
  282. '", actual "' + Test.Unit.inspect(actual) + '"'); }
  283. catch(e) { this.error(e); }
  284. },
  285. assertInspect: function(expected, actual) {
  286. var message = arguments[2] || "assertInspect";
  287. try { (expected == actual.inspect()) ? this.pass() :
  288. this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
  289. '", actual "' + Test.Unit.inspect(actual) + '"'); }
  290. catch(e) { this.error(e); }
  291. },
  292. assertEnumEqual: function(expected, actual) {
  293. var message = arguments[2] || "assertEnumEqual";
  294. try { $A(expected).length == $A(actual).length &&
  295. expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
  296. this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
  297. ', actual ' + Test.Unit.inspect(actual)); }
  298. catch(e) { this.error(e); }
  299. },
  300. assertNotEqual: function(expected, actual) {
  301. var message = arguments[2] || "assertNotEqual";
  302. try { (expected != actual) ? this.pass() :
  303. this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
  304. catch(e) { this.error(e); }
  305. },
  306. assertIdentical: function(expected, actual) {
  307. var message = arguments[2] || "assertIdentical";
  308. try { (expected === actual) ? this.pass() :
  309. this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
  310. '", actual "' + Test.Unit.inspect(actual) + '"'); }
  311. catch(e) { this.error(e); }
  312. },
  313. assertNotIdentical: function(expected, actual) {
  314. var message = arguments[2] || "assertNotIdentical";
  315. try { !(expected === actual) ? this.pass() :
  316. this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
  317. '", actual "' + Test.Unit.inspect(actual) + '"'); }
  318. catch(e) { this.error(e); }
  319. },
  320. assertNull: function(obj) {
  321. var message = arguments[1] || 'assertNull'
  322. try { (obj==null) ? this.pass() :
  323. this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
  324. catch(e) { this.error(e); }
  325. },
  326. assertMatch: function(expected, actual) {
  327. var message = arguments[2] || 'assertMatch';
  328. var regex = new RegExp(expected);
  329. try { (regex.exec(actual)) ? this.pass() :
  330. this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
  331. catch(e) { this.error(e); }
  332. },
  333. assertHidden: function(element) {
  334. var message = arguments[1] || 'assertHidden';
  335. this.assertEqual("none", element.style.display, message);
  336. },
  337. assertNotNull: function(object) {
  338. var message = arguments[1] || 'assertNotNull';
  339. this.assert(object != null, message);
  340. },
  341. assertType: function(expected, actual) {
  342. var message = arguments[2] || 'assertType';
  343. try {
  344. (actual.constructor == expected) ? this.pass() :
  345. this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
  346. '", actual "' + (actual.constructor) + '"'); }
  347. catch(e) { this.error(e); }
  348. },
  349. assertNotOfType: function(expected, actual) {
  350. var message = arguments[2] || 'assertNotOfType';
  351. try {
  352. (actual.constructor != expected) ? this.pass() :
  353. this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
  354. '", actual "' + (actual.constructor) + '"'); }
  355. catch(e) { this.error(e); }
  356. },
  357. assertInstanceOf: function(expected, actual) {
  358. var message = arguments[2] || 'assertInstanceOf';
  359. try {
  360. (actual instanceof expected) ? this.pass() :
  361. this.fail(message + ": object was not an instance of the expected type"); }
  362. catch(e) { this.error(e); }
  363. },
  364. assertNotInstanceOf: function(expected, actual) {
  365. var message = arguments[2] || 'assertNotInstanceOf';
  366. try {
  367. !(actual instanceof expected) ? this.pass() :
  368. this.fail(message + ": object was an instance of the not expected type"); }
  369. catch(e) { this.error(e); }
  370. },
  371. assertRespondsTo: function(method, obj) {
  372. var message = arguments[2] || 'assertRespondsTo';
  373. try {
  374. (obj[method] && typeof obj[method] == 'function') ? this.pass() :
  375. this.fail(message + ": object doesn't respond to [" + method + "]"); }
  376. catch(e) { this.error(e); }
  377. },
  378. assertReturnsTrue: function(method, obj) {
  379. var message = arguments[2] || 'assertReturnsTrue';
  380. try {
  381. var m = obj[method];
  382. if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
  383. m() ? this.pass() :
  384. this.fail(message + ": method returned false"); }
  385. catch(e) { this.error(e); }
  386. },
  387. assertReturnsFalse: function(method, obj) {
  388. var message = arguments[2] || 'assertReturnsFalse';
  389. try {
  390. var m = obj[method];
  391. if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
  392. !m() ? this.pass() :
  393. this.fail(message + ": method returned true"); }
  394. catch(e) { this.error(e); }
  395. },
  396. assertRaise: function(exceptionName, method) {
  397. var message = arguments[2] || 'assertRaise';
  398. try {
  399. method();
  400. this.fail(message + ": exception expected but none was raised"); }
  401. catch(e) {
  402. ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e);
  403. }
  404. },
  405. assertElementsMatch: function() {
  406. var expressions = $A(arguments), elements = $A(expressions.shift());
  407. if (elements.length != expressions.length) {
  408. this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
  409. return false;
  410. }
  411. elements.zip(expressions).all(function(pair, index) {
  412. var element = $(pair.first()), expression = pair.last();
  413. if (element.match(expression)) return true;
  414. this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
  415. }.bind(this)) && this.pass();
  416. },
  417. assertElementMatches: function(element, expression) {
  418. this.assertElementsMatch([element], expression);
  419. },
  420. benchmark: function(operation, iterations) {
  421. var startAt = new Date();
  422. (iterations || 1).times(operation);
  423. var timeTaken = ((new Date())-startAt);
  424. this.info((arguments[2] || 'Operation') + ' finished ' +
  425. iterations + ' iterations in ' + (timeTaken/1000)+'s' );
  426. return timeTaken;
  427. },
  428. _isVisible: function(element) {
  429. element = $(element);
  430. if(!element.parentNode) return true;
  431. this.assertNotNull(element);
  432. if(element.style && Element.getStyle(element, 'display') == 'none')
  433. return false;
  434. return this._isVisible(element.parentNode);
  435. },
  436. assertNotVisible: function(element) {
  437. this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
  438. },
  439. assertVisible: function(element) {
  440. this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
  441. },
  442. benchmark: function(operation, iterations) {
  443. var startAt = new Date();
  444. (iterations || 1).times(operation);
  445. var timeTaken = ((new Date())-startAt);
  446. this.info((arguments[2] || 'Operation') + ' finished ' +
  447. iterations + ' iterations in ' + (timeTaken/1000)+'s' );
  448. return timeTaken;
  449. }
  450. }
  451. Test.Unit.Testcase = Class.create();
  452. Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
  453. initialize: function(name, test, setup, teardown) {
  454. Test.Unit.Assertions.prototype.initialize.bind(this)();
  455. this.name = name;
  456. if(typeof test == 'string') {
  457. test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
  458. test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
  459. this.test = function() {
  460. eval('with(this){'+test+'}');
  461. }
  462. } else {
  463. this.test = test || function() {};
  464. }
  465. this.setup = setup || function() {};
  466. this.teardown = teardown || function() {};
  467. this.isWaiting = false;
  468. this.timeToWait = 1000;
  469. },
  470. wait: function(time, nextPart) {
  471. this.isWaiting = true;
  472. this.test = nextPart;
  473. this.timeToWait = time;
  474. },
  475. run: function() {
  476. try {
  477. try {
  478. if (!this.isWaiting) this.setup.bind(this)();
  479. this.isWaiting = false;
  480. this.test.bind(this)();
  481. } finally {
  482. if(!this.isWaiting) {
  483. this.teardown.bind(this)();
  484. }
  485. }
  486. }
  487. catch(e) { this.error(e); }
  488. }
  489. });
  490. // *EXPERIMENTAL* BDD-style testing to please non-technical folk
  491. // This draws many ideas from RSpec http://rspec.rubyforge.org/
  492. Test.setupBDDExtensionMethods = function(){
  493. var METHODMAP = {
  494. shouldEqual: 'assertEqual',
  495. shouldNotEqual: 'assertNotEqual',
  496. shouldEqualEnum: 'assertEnumEqual',
  497. shouldBeA: 'assertType',
  498. shouldNotBeA: 'assertNotOfType',
  499. shouldBeAn: 'assertType',
  500. shouldNotBeAn: 'assertNotOfType',
  501. shouldBeNull: 'assertNull',
  502. shouldNotBeNull: 'assertNotNull',
  503. shouldBe: 'assertReturnsTrue',
  504. shouldNotBe: 'assertReturnsFalse',
  505. shouldRespondTo: 'assertRespondsTo'
  506. };
  507. var makeAssertion = function(assertion, args, object) {
  508. this[assertion].apply(this,(args || []).concat([object]));
  509. }
  510. Test.BDDMethods = {};
  511. $H(METHODMAP).each(function(pair) {
  512. Test.BDDMethods[pair.key] = function() {
  513. var args = $A(arguments);
  514. var scope = args.shift();
  515. makeAssertion.apply(scope, [pair.value, args, this]); };
  516. });
  517. [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
  518. function(p){ Object.extend(p, Test.BDDMethods) }
  519. );
  520. }
  521. Test.context = function(name, spec, log){
  522. Test.setupBDDExtensionMethods();
  523. var compiledSpec = {};
  524. var titles = {};
  525. for(specName in spec) {
  526. switch(specName){
  527. case "setup":
  528. case "teardown":
  529. compiledSpec[specName] = spec[specName];
  530. break;
  531. default:
  532. var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
  533. var body = spec[specName].toString().split('\n').slice(1);
  534. if(/^\{/.test(body[0])) body = body.slice(1);
  535. body.pop();
  536. body = body.map(function(statement){
  537. return statement.strip()
  538. });
  539. compiledSpec[testName] = body.join('\n');
  540. titles[testName] = specName;
  541. }
  542. }
  543. new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
  544. };